Static files are a common source of confusion for Django newcomers. The term "static files" refers to files like CSS, JavaScript, or images that do not change in a web app. They remain static. For local development, static files are served up by the local Django web server and minimal configuration is required. Django does not support serving static files in production, however, so a number of additional configurations are required to get them working.
In this tutorial, we'll look at how static files work in Django, how to configure them locally, as well as using WhiteNoise to serve them in production.
Local Development
When you first run startproject
to create a new Django project, a settings.py
file is automatically created which defaults to local development settings. These include DEBUG
set to True
and DATABASES
set to SQLite. It also includes, near the bottom of the file, a solitary line for static files:
# settings.py
STATIC_URL = '/static/'
The STATIC_URL
setting controls the actual URL used to locate static files. In this case, that would be /static/
, so in local development, 127.0.0.1:8000/static/
or localhost:8000/static/
. In production, if our website was called example.com
, the static files would be located at example.com/static/
.
There are two steps required to use static files locally:
- create a
static
directory - add
{% load static %}
at the top of a template and use thestatic
template tag
The static
directory should be in the same folder as manage.py
. Add any desired static files here. For example, you might create another folder called css
and within it a file called base.css
. To include this file in a template, make sure the first line of the file is {% load static %}
and the link would look something like this: <link href="{% static 'css/base.css' %}" rel="stylesheet">
. The static
template tag is used variable, could also hardcode the result here. This is useful later on for production.
That's it!
collectstatic
For production, Django looks through the entire project for static files (STATICFILES_DIRS
) and collects all available static files via the collectstatic
command into a dedicated directory (STATIC_ROOT
). The way in which the files are stored is dictated by STATICFILES_STORAGE
. We therefore need to add these three additional configurations and run the collectstatic
command in production every time there is a change to the static files.
STATICFILES_DIRS
tells Django where to look for static files in a Django project. Often, this is simply the static
directory but there could be static directories in various apps or other locations. When the collectstatic
command is run, Django refers to the list of directories here when creating a dedicated directory for production serving.
STATIC_ROOT
is the location of the collected static files, often named staticfiles
.
STATICFILES_STORAGE
is the file storage engine used when collecting static files with the collecstatic
command. By default, it is implicitly set to django.contrib.staticfiles.storage.StaticFilesStorage
.
In your local Django project, add these three settings to your settings.py
file.
# settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'),] # new
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') # new
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage' # new
Then run the command python manage.py collectstatic
.
(env) $ python manage.py collectstatic
You have requested to collect static files at the destination
location as specified in your settings.
This will overwrite existing files!
Are you sure you want to do this?
Type 'yes' to continue, or 'no' to cancel:
Type yes
to continue and hit the Enter key. A new staticfiles
directory will be created which has folders for admin
(the built-in admin has its own static files), staticfiles.json
, and whatever directories are in your static
folder.
If you now add a new static file to static
, it will be available for local usage. It is only for production where the file won't be present, unless you run python manage.py collectstatic
each and every time. For this reason, running collectstatic
is typically added to deployment pipelines and is done by default on Heroku.
WhiteNoise
Even though we've configured our Django project to collect static files properly, there's one more step involved which is not included in the official Django docs. That is the configuration of WhiteNoise, radically simplified static file serving for Python web apps. Is it possible to use another package to serve Python static files? Yes. Have I ever seen it done over the last 5 years? No. Everyone uses WhiteNoise and you should, too, since Django won't serve static files in production on its own.
(env) $ pipenv install whitenoise==5.1.0
Then in the settings.py
file, add whitenoise
to the INSTALLED_APPS
above the built-in staticfiles
app. Under MIDDLEWARE
, add a new WhiteNoiseMiddleware
on the third line. And at the bottom of the file, change STATICFILES_STORAGE
to use WhiteNoise. It should look like the following:
# settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'whitenoise.runserver_nostatic', # new
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware', # new
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
...
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'),]
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' # new
That's it! Run python manage.py collectstatic
again so that the files are stored using WhiteNoise. And then deploy with confidence to the hosting platform of your choice.
Top comments (2)
Good Explanation. You are helping me a lot.
Just the information I've been looking for! Thanks! How does
/media/
figure into collectstatic? I am not even sure I am asking the right question, except the two confuse me to no end.