Understanding static files in Django + Heroku
Understanding Django’s static files is a bit confusing. There is certainly quite a bit of documentation and variables out there, so I’m writing this for my future self.
This is an important setting for the entire app. You want this to be
False on production. If
DEBUG = True then
./manage.py runserver will handle serving static files on its own. However, when
DEBUG = False
runserver will not do this for you, and that’s where some complexity arises.
The static url is the url path where a client or browser can access static files. So with
https://www.example.com as your url, if
STATIC_URL = 'mystaticpath'and you had an image named
test.jpg, you would access the image at
This generates the directory where files static files are placed when you run
./manage.py collectstatic. For example, if your root folder was
STATIC_ROOT = os.path.join(BASE_DIR, 'placefileshere'), after running
collectstatic a folder would have been generated in
staticfiles.json and also
test.jpg from above located in it.
I do not believe
STATIC_ROOT has any impact when
DEBUG = True, as you don’t need to run
collectstatic in development.
This determines the locations where static files are being pulled from. For example:
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'polls/files/')]
Any files in
polls/files/ would be in and accessible as static files. The directory structure within it is maintained as well, so for example with
--polls ----files ------img --------test.jpg
STATIC_URL = 'mystaticpath' you would access
test.jpg by going to
STATICFILES_DIRS will merge them all into one, e.g.:
STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'polls/files/'), os.path.join(BASE_DIR, 'people/otherfiles/') ]
--polls ----files ------img --------test.jpg --people ----otherfiles ------css --------styles.css
If you ran collectstatic you’d end up with
--placefileshere ----img ------test.jpg ----css ------styles.css
and likewise access the css file at
https://www.example.com/mystaticpath/css/styles.css. Due to the merging nature of this, you have to be wary of namespacing conflicts.
Note: if you’re not deploying to Heroku, you can ignore this section.
Deploying to Heroku adds additional complexity to the process of static file handling. They have a some docs about here, however, there’s actually a few additional things going on.
Chances are you’re using the
django-heroku library and setting
django_heroku.settings(locals()) as suggested in Configuring Django Apps for Heroku.
What I’ve discovered is that
STATIC_ROOT are actually overwritten by heroku to
STATIC_URL = '/static/' and
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles'). Even if you have a different
STATIC_ROOT in your settings if you were to run
heroku run python manage.py collectstatic, they will use
staticfiles as the
The suggestion in their docs does not impact, but is just matching the settings locally to what Heroku is overriding it to.
Static files are a relatively complicated thing in Django. There are a lot of moving parts, and I’ve linked many of the docs. If you’re interested in digging deeper, I’d suggest looking at WhiteNoise, a library that helps with static file serving for python.