Stop using STATIC_URL and start using the static template tag to ensure your URLs work across storages.

What does that mean? It means when you have static files in templates, do this:

{% load static from staticfiles %} <img src=“{% static "images/hi.jpg” %}“ alt="Hi!” />

Not this:

<img src=“{{ STATIC_URL }}images/hi.jpg” alt=“Hi!” />

Why? Like many of the worst mistakes to make, this will work absolutely fine until it doesn’t.

So there you are, writing your templates. You think “what’s the point in loading an extra template tag when I already have STATIC_URL in the context?”. You collectstatic your files to somewhere your nginx can serve them, and everything works fine. Later you try moving to Amazon S3, or CloudFront, or some other CDN. At which point all your static links break, and there goes your afternoon.

Why?

What the static tag does is call the url method of your configured STATICFILES_STORAGE (remember those from http://blog.doismellburning.co.uk/2012/06/25/django-and-static-files/ ?) with the path provided, to get the correct URL for the file path.

For the vanilla StaticFilesStorage , this is straightforward, so StaticFileStorage.url effectively just prepends STATIC_URL to the path given, which is almost the same result as blind in-template concatenation.

For some of the more complex storages, like S3BotoStorage from django-storages , determining a file’s URL can potentially involve generating an access token, expiry time, and other such fun. At this point, simple concatenation won’t quite cut it.

Anyone who’s been using Django for some time may be particularly in need of this advice, as, prior to Django 1.5, the documentation advocated just using STATIC_URL , only mentioning the template tag as a sidenote, and it generally works often enough that you could never realise the shortcomings until they come to bite you.

Don’t get caught out.

UPDATE: