Understanding static files in Django + Heroku


Ryan von Kunes Newton

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.

The important file here is the settings.py file that every Django project has. We’ll dig into DEBUGSTATIC_ROOTSTATIC_URL, and STATICFILES_DIRS.


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 withhttps://www.example.com as your url, if STATIC_URL = 'mystaticpath'and you had an image named test.jpg, you would access the image at https://www.example.com/mystaticpath/test.jpg.


This generates the directory where files static files are placed when you run ./manage.py collectstatic. For example, if your root folder was myawesomesite and STATIC_ROOT = os.path.join(BASE_DIR, 'placefileshere'), after running collectstatic a folder would have been generated in myawesomesite/placefileshere/ with 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


and STATIC_URL = 'mystaticpath' you would access test.jpg by going tohttps://www.example.com/mystaticpath/img/test.jpg.

Having multiple STATICFILES_DIRS will merge them all into one, e.g.:

    os.path.join(BASE_DIR, 'polls/files/'),
    os.path.join(BASE_DIR, 'people/otherfiles/')



If you ran collectstatic you’d end up with


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.

Involving Heroku

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_URL and 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 STATIC_ROOT.

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.

Django Replies 0 Views 103 Users 1

Suggested Topics

Django Updated on April 1, 2020, 9:12 p.m. Replies Views 55

Django Updated on April 6, 2020, 12:50 a.m. Replies Views 42

Django Updated on April 7, 2020, 9:54 p.m. Replies Views 73

Django Updated on April 13, 2020, 11:35 a.m. Replies Views 143