It’s becoming more and more important for websites to carefully consider how their resources are cached in users’ browsers. Get the caching wrong, and you either end up with a woefully slow experience for the user, or a very strange looking website as users are left with stale CSS files and images.
Or often both.
Caching headers in Django
Telling the browser how long to cache a resource is done with one of two headers:
Cache-Control: In HTTP/1.1, this can set the maximum age before a resource should be re-downloaded.
Expires: In the older HTTP/1.0 standard, this sets the date and time that a resource becomes outdated and should be refreshed.
To control these headers in Django is less simple than you might think. If you’re happy to use the cache framework then it will take care of these headers for you, but as we have a separate Squid cache in front of our application, this was a more heavyweight solution than we needed.
Modifying HTML responses using View classes
In our case, all of our HTML pages are served with an extended version of the TemplateView class:
To add headers, we need to modify the
HTTPResponse, which we can intercept by extending the
Django also provides
patch_response_headers a handy helper function to generate our caching headers for us and attach them to the response:
And now we can see our extra caching headers in the HTTP response:
Browsers and proxies will now cache the HTML pages for 5 minutes.
Controlling caching for static files
Django recommends serving static files separately from the rest of your application.
However, for simplicity and dev-prod parity we’ve been using DJ-Static to serve static files with the Django WSGI app, as introduced by Kenneth Reitz. This was also, at the time we implemented it, the method recommended by Heroku for managing static files in Django.
Serving static files with WhiteNoise is pretty simple (as it was with DJ-Static):
WhiteNoise will add a
Cache-Control header, although it doesn’t support set the older
Expires header. By default, the
Cache-Control header is initially set to no caching:
We wanted our static files to be cached for a year, so we set the
WHITENOISE_MAX_AGE setting in
This will set the
max-age in the
Cache-Control header to achieve the browser caching we’re looking for:
Now we have control
Leveraging browser caching is an invaluable tool in performance, and so understanding how we can control the user’s cache with Django is very helpful.
Hopefully I’ve demonstrated some ways that this can be achieved, which we’ve just implemented on cn.ubuntu.com.
Also published on my blog.