Cache busting CSS with query strings

Because visitors' browsers locally store a cached copy of the static files, there is a possibility for them not to see the changes when you update your site. This can be troublesome, so we need a cache busting strategy to clear cache and to make the browser download a fresh copy of the static assets.

By using a query string, we can tell the browser that there is a new version of the file. I used to add the version to CSS file like this:

<link href="{{ STATIC_URL }}css/base.css?v=1.4" rel="stylesheet" type="text/css">

However, it is easy to forget to change the query string manually every time. So I started to find the other way to automate CSS versioning.

Django does it for us

Instead of adding a query string, changing the file name itself is also working for cache busting. There is ManifestStaticFilesStorage in Django that appends the MD5 hash of the file’s content to the filename. For example, the css/base.css file would also be saved as css/base.e352ca3230fc.css.

To enable the ManifestStaticFilesStorage, you have to meet the following requirements:

  • the STATICFILES_STORAGE setting is set to 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'.
  • the DEBUG setting is set to False.
  • you’ve collected all your static files by using the collectstatic management command.
# settings.py
DEBUG = False
...
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'

And add the static template tag to build the URL for the given relative path using the configured STATICFILES_STORAGE.

<!-- base.html -->
{% load static from staticfiles %}
...
<link href="{% static 'css/base.css' %}" rel="stylesheet" type="text/css">

With this changes, the static filename will be replaced automatically with hashed name one when run collectstatic.

ManifestFilesMixin support in django-storages

Because Xoxzo is using S3BotoStorage, we need a special mixin to use ManifestStaticFilesStorage. django-storages supports ManifestFilesMixin, and you can add it to your storage class.

# storage.py
from storages.backends.s3boto import S3BotoStorage as S3BotoStorageOrig
from django.contrib.staticfiles.storage import ManifestFilesMixin

class S3BotoStorage(S3BotoStorageOrig):
    ...

class ManifestS3Storage(ManifestFilesMixin, S3BotoStorage):
    pass
# settings.py
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
STATICFILES_STORAGE = 'storage.ManifestS3Storage'

Congratulations! Now you can get the hashed CSS file!

<link href="../css/base.e352ca3230fc.css" rel="stylesheet" type="text/css">
Hyejeong Park

Hyejeong Park

User Experience

Joined November 2016. Major in Visual Design at Seoul National University. Previously worked as an UI designer for iOS app. Interested in both design and web development. Loves beautiful UI, clean Web, and Python/Django.