No nudity please, we're Google (or why you shouldn't mix naked domains and www on Google App Engine)

I have to confess, I love naked domains. You might say, I have somewhat of a fetish.

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.

Comments