No nudity please, we’re Google (or why you shouldn’t mix naked domains and www on Google App Engine)
Naked domains, of course, are domain names without the www prefix. So, instead of www.singularity08.com, for example, having singularity08.com.
One of my pet peeves are sites that don't display correctly without the www prefix. I've found that it's usually a good sign that the site is going to be pretty crap. In fact, I was hoping that some day we would have www disappear from use altogether and that we'd all be swimming in a sea of naked domains. Well, I almost got my wish -- we've at least got heaps of domains with nudity.
The truth is, however, that the www subdomain is not going anywhere, especially on the cloud, and the thing to do is to have your naked domain forward to www (you listening, no-www?)
Why GAE naked domains don't play nice with others
When you host with Google App Engine, you can choose to use your own domain name for your app. You do this through Google Apps (Confused? You should be. The two sound very similar.) Google Apps, of course, is Google's online office suite. You get a Google Apps account and create A-records for your naked domain (four of them, pointing to 216.239.32.21, 216.239.34.21, 216.239.36.21, and 216.239.38.21) and you create a CNAME for your www subdomain that points to ghs.google.com. (I use DynDNS for all my DNS hosting -- they rock -- and make it really easy to set this stuff up.)
If all this DNS voodoo sounds confusing, it's because it is. I still wouldn't know a CNAME from an A-Record if I met one in a dimly-lit alley (ok, maybe I'd recognize the A-Record if it wasn't wearing make-up -- just maybe though). All you really need to know is that if you use those settings, things will work thanks to the magic of those hard-working DNS gnomes. No really, they exist. They're cute little things too, all bright-eyed and furry.
The problem you're left with, however, is that your domain is reachable from two URLs: one using the naked domain and one using the www.
"So what's wrong with that?", I hear you ask. Ah, a number of things, my inquisitive fellow, a number of things...
Firstly, it's not good to have two sets of URLs for the same resource (if you don't have time to digest the in-depth reasons why, at least know that it's bad for search engine rankings.)
Secondly, that address that you set your CNAME to, ghs.google.com, does special things. Like load balance your requests among the hundreds thousands gazillions of servers that Google has in its cloud. Your puny "A" list is not going to compete with that. In other words: www 1, naked domain 0.
Finally, and most importantly, your app will break. Woah, that's a big one... care to explain, Aral. Sure, Aral, I thought you'd never ask (it's not considered talking to yourself if you do it in a blog post, you know!)
Take this scenario:
You hit the Singularity web site at http://singularity08.com. Next, you go to buy a ticket and you get forwarded to PayPal. In the forwarding URL, PayPal gets told to return you to http://www.singularity08.com. Not a big deal, right? Oooh, but it is. When you return, you end up losing your session. Ouch!
You can work around this by making sure that you always use request.META['HTTP_HOST'] in Django when creating callback URLs but I guarantee you that you'll forget at some point and mix your naked domain and www and end up scratching your head at the random errors that result. That's exactly what happened to me earlier this week while gearing up to launch the Singularity web site.
So how do you work around it?
The simplest way I found was to write a simple piece of middleware in Django to handle the forwarding. Here it is, released under the MIT license:
import os from django.conf import settings from django import http class NakedDomainRedirectMiddleware(object): def process_request(self, request): """ If the domain is being accessed from the naked domain, forward it to www. Copyright (c) 2008, Aral Balkan, Singularity Web Conference (http://www.singularity08.com) Released under the open source MIT License. """ naked_domain = settings.NAKED_DOMAIN host_name = os.environ['HTTP_HOST'] start_of_uri = host_name[0:len(naked_domain)] if start_of_uri == naked_domain: full_path = request.get_full_path() uri = 'http://www.' + naked_domain + full_path; return http.HttpResponsePermanentRedirect(uri)
Save the above class and then add it to your settings file, at the top of your MIDDLEWARE_CLASSES tuple. For example, I have it in a module called middleware:
MIDDLEWARE_CLASSES = (
'middleware.NakedDomainRedirectMiddleware',
'django.middleware.common.CommonMiddleware',
# etc.
)
Finally, set your naked domain in the settings file:
NAKED_DOMAIN = 'singularity08.com'
This should forward all requests to the naked domain to www. You'll end up not having two sets of URLs for each resource and you'll save yourself a lot of headache.
Google is aware of this issue and they were trying to implement a fix on their end to help me out but that's not in place yet. It's possible that they may implement the fix and make it the default behavior for all accounts (which is what I think should be the case) but it may take a little while as any such change will have to go through full QA testing.
In the meanwhile, this is a stop gap measure that's working out fine for me currently on the Singularity web site. I hope it helps you out too.
The No nudity please, we’re Google (or why you shouldn’t mix naked domains and www on Google App Engine) article by Aral Balkan, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-Noncommercial 2.0 UK: England License.
Subscribe to my blog






James Bennett
I don’t know if Google’s App Engine has done something screwy that makes this not work, but the standard way to do this in Django is to enable the CommonMiddleware (which you’ve done in your sample) and also have the setting PREPEND_WWW be True.
Documentation is here:
http://www.djangoproject.com/documentation/settings/#prepend-www
July 21st, 2008 at 9:58 amFraser
Meh, I’m of the opinion that a canonical domain name is good - whether it’s got www on it, it’s naked or you put a completely different subdomain on it - you just need to make sure there’s one true location for your website/service/etc. to live at and that anything people might reference it as aliases to the same place and permanently redirects there.
If you’re running Apache with mod_rewrite you can get this to do it for you:
RewriteEngine on
RewriteBase /
RewriteCond %{HTTP_HOST} !^www.somedomain.com [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteRule ^(.*) http://www.somedomain.com/$1 [R=301]
put in a .htaccess or a virtual host config this will take any request that’s somehow managed to land on your virtual host (e.g. http://somedomain.com/somepage.html or http://www.some-domain.com/somepage.html) will be redirected to the same place on the canonical domain: i.e. http://www.somedomain.com/somepage.html
Handy for when you have umpteen (mis)spelling aliases of your domain name all configured for the same virtual host.
July 21st, 2008 at 3:48 pmFraser
Bah, that should be /$1 not /1 in the RewriteRule line…
July 21st, 2008 at 3:51 pmAral
Hi James,
I tried the Django CommonMiddleware option but it simply prefixes any URL with “www.” which is not what I want when testing from IP addresses and from localhost. The code in question is:
if (settings.PREPEND_WWW and old_url[0] and not old_url[0].startswith('www.')): new_url[0] = 'www.' + old_url[0]July 21st, 2008 at 7:38 pmJames Bennett
Generally, what I do for situations like that is place something in my settings file like so:
PREPEND_WWW = True
if DEBUG:
PREPEND_WWW = False
This way the behavior is turned off when I’m developing (e.g., running on an IP address or localhost), but automatically switches on when I’m running in production on my live domain.
I also tend to tweak a few other settings this way — for example, I often change the cache settings to the ‘dummy’ backend (which does no caching) when in development, so that I don’t see misleading results due to caching; as soon as I flip DEBUG off, however, caching kicks in to give me the performance boost I need in production.
July 22nd, 2008 at 1:19 amJames Bennett
Also, note that an even simpler way to handle this is to place your “development” settings in their own file — say, ‘dev_settings.py’ — in the same directory as your project settings, and then simply place this at the bottom of the project’s settings file:
if DEBUG:
from dev_settings import *
This lets you maintain cleaner separation of your development and production settings, while still maintaining the convenience of just flipping DEBUG on and off to switch between modes.
July 22nd, 2008 at 1:21 amSuplementy
very interesting observations
July 29th, 2008 at 10:12 pmJussi
Seriously helpful. Thank you! I bet a lot of developers run into this. Google should consider linking to this post until they have some solution/documentation on this.
August 4th, 2008 at 7:45 pmAndrew
Do I have to add a new url to the GAE dashboard? I have a http://www.mydomain.com there but do I need to add the naked one? I try and I get “Required field must not be blank ”
I have the CNAME set and the www works, I added the 4 A name ips but naked doesn’t work. Any ideas?
August 31st, 2008 at 9:05 pmAndrew
oh, i didn’t have google apps set to “current version” and I was entering the empty string I think vs the domain. now it works
August 31st, 2008 at 9:57 pmSarah .aka. Mamalotsoftots
Thanks so much for the definition of naked domain. Totally lost me on it.
February 16th, 2009 at 7:49 pmBetty
How did you make domain.com/folder redirected to http://www.domain.com/folder in the first place? It keeps redirecting to http://www.domain.com. Please help me.
March 4th, 2009 at 8:12 amAral
Hey Betty, I believe Google changed this after I wrote the blog post and, AFAIK, aren’t supporting naked domains any longer. I haven’t set up a new project on there since to check.
March 5th, 2009 at 3:43 pmBetty
Thanks Aral~
March 6th, 2009 at 12:52 amLaurent
Aral, I have the same issue as Betty.
May 26th, 2009 at 7:35 pmI’m using GoDaddy to redirect keeness.net to http://www.keeness.net, but then if my users type keeness.net/folder, my appengine app doesn’t see /folder.
Is there a way to get that back? (referer, other?)