Django Static Files
Updated
Table of Contents
Static files like CSS, JavaScript, and fonts are a core piece of any modern web application. They are also typically confusing for Django newcomers since Django provides tremendous flexibility around how these files are used. This tutorial will demonstrate current best practices for configuring static files in both local development and production.
Local Development
For local development, the Django web server automatically serves static files, and minimal configuration is required. A single Django project often contains multiple apps, and by default, Django will look within each app for a static
directory containing static files.
For example, if one of your apps was called blog
and you wanted to add a CSS file to it called base.css
, you would need first to create a new directory called blog/static
and then add the file within it at the location blog/static/style.css
.
However, since most Django projects involve multiple apps and shared static files, it is common on larger projects to create a base-level folder instead, typically named static
. This can be added from the command line with the mkdir
command:
(.venv) $ mkdir static
For demonstration purposes, let's also add a base.css
file. Assuming you had only just started a new Django project with the startproject
command, your directory structure would now look like this:
├── django_project
│ ├── __init__.py
| ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
├── static
│ ├── base.css
Within the settings.py
file, near the bottom, there is a single line of configuration for static files called STATIC_URL, which is the location of static files in our project.
# django_project/settings.py
STATIC_URL = "static/"
All static files will be stored in the location http://127.0.0.1:8000/static/
or http://localhost:8000/static/
. And if you wanted to access the base.css
file, its location would be http://127.0.0.1:8000/static/base.css
or http://localhost:8000/static/base.css
.
Loading Static Files into Templates
Loading static files in a template is a two-step process:
- add
{% load static %}
at the top of the template - add the {% static %} template tag with the proper link
There are two main ways to structure templates in a Django project, as outlined in this tutorial. Let's assume we use a templates/base.html
file for a Blog project. To add our static base.css
file to it we'd start by adding {% load static %}
at the top of the file and then use the {% static %}
tag with the path to it. Since the STATIC_URL
is already set to /static/
, we don't need to write the full route out of static/base.css
and can instead shorten it to base.css
.
<!-- templates/base.html -->
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>Learn Django</title>
<link rel="stylesheet" href="{% static 'base.css' %}">
</head>
...
</html>
You'll see the changes if you save and reload the web page. Adding links for either images in an img
folder or JavaScript in a js
folder would look as follows:
<img src="{% static 'img/path_to_img' %}">
<script src="{% static 'js/base.js' %}"></script>
collectstatic
Serving static files in production requires several additional steps, where confusion typically arises for Django newcomers. Local development is designed to keep things nice and easy but is far from performant. In a production environment, combining all static files in the Django project into one location and serving that a single time is more efficient.
Django comes with a built-in management command, collectstatic, that does this for us.
We need three more configurations in our settings.py
file before collectstatic
works properly. The first is STATICFILES_DIRS, which defines additional locations, if any, the staticfiles app should look within when running collectstatic
. In our simple example, the only location for local files is the static
directory, so we will set that now.
# django_project/settings.py
STATIC_URL = "static/"
STATICFILES_DIRS = [BASE_DIR / "static"] # new
Next up is STATIC_ROOT which sets the absolute location of these collected files, typically called staticfiles
. In other words, when collecstatic
is run locally, it will combine all available static files, as defined by STATICFILES_DIRS
, and place them within a directory called staticfiles
. This is how we set that configuration.
# settings.py
STATIC_URL = "static/"
STATICFILES_DIRS = [BASE_DIR / "static"]
STATIC_ROOT = BASE_DIR / "staticfiles" # new
The final step is STORAGES, a dictionary of all the storages to be used with Django. By default, it is implicitly set with a default
configuration for files and staticfiles
for managing static files. Let's make that explicit for now in our django_project/settings.py
file.
# settings.py
STATIC_URL = "static/"
STATICFILES_DIRS = [BASE_DIR / "static"]
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
},
}
Now we can run the command python manage.py collectstatic
which will create a new staticfiles
directory.
(.venv) $ python manage.py collectstatic
If you look within it you'll see that staticfiles
also 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 the static
directory it will immediately 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.
As a brief recap:
STATIC_URL
is the URL location of static files located inSTATIC_ROOT
STATICFILES_DIRS
tells Django where to look for static files in a Django project, such as a top-levelstatic
folderSTATIC_ROOT
is the folder location of static files whencollectstatic
is runSTORAGES
and specifically thestaticfiles
alias control the engine used when collecting static files with thecollectstatic
command
Production
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 to serve the static files in production. The first step is to install the latest version of the package:
(.venv) $ python -m pip install whitenoise==6.0.0
Then in the django_project/settings.py
file make three changes:
- add
whitenoise
to theINSTALLED_APPS
above the built-instaticfiles
app - under
MIDDLEWARE
, add a newWhiteNoiseMiddleware
on the third line - change
STORAGES
to useWhiteNoise
.
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 = [BASE_DIR / "static"]
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", # new
},
}
That's it! Run python manage.py collectstatic
again to store the files using WhiteNoise. Then, deploy with confidence to the hosting platform of your choice.
CDNs
Content Delivery Networks (CDNs) are helpful on very high-traffic sites where performance is a concern. Rather than serving static files from your Django server, you post them on a dedicated CDN network and then call them. The official WhiteNoise documentation has additional instructions.
Conclusion
Configuring static files is a core part of any Django project. If you'd like to learn more about working with static and media files, check out the Django File/Image Uploads Tutorial.