Friday, July 12, 2013

Cookie-less Domain and Static Files versioning with Google App Engine

Cookieless domains are one of the must have optimization when you are serving a lot of static files. The reason is that if you have a cookie in the same domain it sends those cookies in the request headers if you are serving your static files on the same domain. Those build up and unnecessary. If you have your cookie in domain level and using appspot domain for your main domain this won't work. This will also version your files so in your app.yaml you can leave (default_expiration: "30d") all the time. Each deploy will prefix your static urls with different version. Here is how I did it:


import os
from google.appengine.api import app_identity

VERSION_ID = os.environ.get('CURRENT_VERSION_ID', '1.1').split('.')
VERSION = VERSION_ID[0]
APP_VERSION = int(VERSION_ID[1])
APP_ID = app_identity.get_application_id()

IS_DEV = os.environ.get('SERVER_SOFTWARE', 'Development/%s' % VERSION_ID).startswith('Dev')
IS_BACKEND = backends.get_backend() is not None

def static_url(num=0):

    if not IS_DEV:
        return 'http://%s' % '.'.join([
            str(num),
            str(APP_VERSION),
            VERSION if not IS_BACKEND else 'cdn',
            app_identity.get_default_version_hostname()
        ])

    return ''


Then in all your static js/css etc prefix all with static_url(1) 2, 3 so it loads in parallel. Modern browsers should now load stuff in parallel but still helpful. The reason this works is cause in app engine you can have multiple versions of your app serving in appspot.com like (version).(app-id).appspot.com so what I did here is get the unique version of currently deployed app and prefix it like: (for-parallel-loading-number).(unique-deployed-app-version).(app-version).(app-id).appspot.com, (unique-deployed-app-version) is a version number that only changes when you do an app deployment so this is what we use for versioning the static file, so this will never change until your app is ready and deployed, but I'm not really sure if about it if this is something that can get used while other instance is not ready and request goes through an instance that is not ready yet.

This is also built-in to my app-engine-starter.