Fix your Django’s /static/ files handling

The problem

Serving static assets in your Django setup can be simpler that it might seem, even for more advanced scenarios (think LESS / SASS precompilers, compressors, remote storages, CDNs, etc.).

First, let’s see what a common /static/ Django setup looks like. It is not that simple but still widely used:

  • django-storages and remote storage setup (e.g. Amazon S3).
  • Custom StaticFilesStorage class to support S3 (probably with additions needed for django-compressor).
  • Precompile / compress / minify and collect static files.
  • Sync static assets to Amazon S3.
  • CDN with S3 as an origin.

You may be asking: Fine, but what could possibly be wrong with that setup?

A lot of things. Some of these steps are just redundant. Also, due to issues with remote storage handling (django-storages and S3, I’m looking at you) statics assets may not be synced correctly, leaving you with outdated files. And in the end, the above setup (at least with default settings) may give you mediocre performance and reliability: wouldn’t it be nice to have assets versioned, automatically gzipped, with far future Expires headers for superior caching?

The fix

Enter the open-source WhiteNoise app which “radically simplifies static file serving for Python web apps”. It’s a smart and easier way to handle static assets. Basically, it acts as a production-ready static file server within your Django app (no Nginx or Apache involved here!), and it works great as a simple and reliable origin for your preferred CDN (e.g. Amazon CloudFront). Now, /static/ setup can look like this:

  • WhiteNoise simple config.
  • Precompile / compress / minify and collect static files.
  • Just point your CDN to your app’s /static/ endpoint.

And the setup is easy:

1. Update WSGI script.
 from django.core.wsgi import get_wsgi_application
 from whitenoise.django import DjangoWhiteNoise

 application = get_wsgi_application()
 application = DjangoWhiteNoise(application)

2. Enable Gzip and versioning support in Django settings.
 STATICFILES_STORAGE =
     'whitenoise.django.GzipManifestStaticFilesStorage'

Note: remember to use {% static %} template tag from {% load static from staticfiles %} in templates, instead of a hardcoded {{ STATIC_URL }} so the static files versioning works correctly.

That’s it! I suppose you have your CDN already configured, as in original “common” setup, but WhiteNoise docs have some additional tips in case you need them.

See the difference? No need for custom StaticFilesStorage and django-storages / S3 setup. There are no issues with assets being out of date, because WhiteNoise is doing file versioning via Django’s ManifestStaticFilesStorage (each change to a static file results in a new unique static asset — thank you, Django 1.7). No need to upload your statics to S3 because CDN will directly fetch them from your Django app now. And better performance: WhiteNoise will set far future Expires headers for statics to cache them properly and they will be gzipped to speed up page loading.