Even a fairly light-weight page will probably load some CSS and JS, and a few images - it’s pretty easy to find each page load triggering at least a dozen subsequent fetches. Now this doesn’t mean the files will be re-fetched - a sensible webserver setup will respond with 304 (Not Modified)
and no content, so the browser will use its cache, but those are requests that don’t need to be made, and only so many that your server will handle at once.
Where static files are concerned, caching is simple: all you need are three easy things:
- Set expiry headers
- Use
CachedStaticFilesStorage
- Set
Cache-Control: public
Set expiry headers
There are two important HTTP headers here: Expires
and Cache-Control: max-age
. They have slightly different semantics, but the distinction is minor - Apache and nginx both provide a single configuration directive to set both. They tell caches how long they can keep files for - you’ll want to set this to a year from access. Why a year? Historically this used to be the maximum allowed value - that restriction has been lifted, but in many cases, there’s little point in setting a larger value, and I don’t consider it worth it.
For Apache, this means first ensuring that mod_expires
is enabled:
$ sudo a2enmod expires
then, in the configuration block serving your static files:
ExpiresActive on
ExpiresDefault “access plus 1 year”
For nginx it’s much simpler (nearly all distros package nginx with the ngx_http_headers_module
included):
expires 1y;
That’s it; now caches should keep your files for a year. Now you’re probably thinking that that’s fine for things that don’t change often, if at all, like images, but what about your CSS and JavaScript that tend to change between releases? This is where the next task comes in:
Use CachedStaticFilesStorage
STATICFILES_STORAGE = ‘django.contrib.staticfiles.storage.CachedStaticFilesStorage’
This is a slightly misleadingly-named class: what it does is rename your static files to include a hash of their content. If the content changes, the filename changes, so clients will get changes immediately rather than waiting for cached content to expire.
The “Cached” part of the name is because it uses the Django cache framework to store the hashes - Django 1.7 brings ManifestStaticFilesStorage
which instead writes them to a JSON file it loads at startup - a much-awaited improvement!
Set Cache-Control: public
There are two types of caches: private (e.g. a user’s browser) and public (e.g. an institutional shared proxy, or a local CDN).
Sending Cache-Control: public
with your static files tells shared caches that it’s ok to retain them.
For Apache, this means ensuring that mod_headers
is enabled:
$ sudo a2enmod headers
then adding the following to the configuration block for your static files:
Header append Cache-Control public
(append
rather than set
so as not to overwrite the Cache-Control: max-age
setting described above; add
would be equally valid)
For nginx:
add_header Cache-Control public
Conclusion
Caching configuration is really useful, and simple to set up. If you’ve not done it yet for your apps, follow this guide and speed things up for your users.