Static & Media Files
Websites typically need to serve additional assets such as images, JavaScript and CSS. Django splits these assets into two categories:
- Static files: CSS, JavaScript and theme images that ship with your code. These are managed by the
django.contrib.staticfilesapp. - Media files: files uploaded by your users at runtime, such as profile pictures or documents.
The two are configured separately and, in production, are usually served in completely different ways.
Configuring Static Files
First, make sure that django.contrib.staticfiles is included in your INSTALLED_APPS list. Next, define the STATIC_URL in your settings file to specify the base URL for these files.
STATIC_URL = "static/"Using the static Template Tag
You can now use the static template tag in your HTML templates to build the correct URL for any given file.
{% load static %}
<img src="{% static 'my_app/example.jpg' %}" alt="My image">If you need the same URL more than once in a template, store it in a variable using the as form:
{% static 'my_app/example.jpg' as example_url %}
<img src="{{ example_url }}" alt="My image">
<a href="{{ example_url }}">Download</a>The same URL can be built from Python code using django.templatetags.static.static. This is useful inside views, forms or model methods where no template is involved.
from django.templatetags.static import static
image_url = static("my_app/example.jpg")Organizing Static Files
You should store your static files in a folder called static within your app directory. For example:
my_app/static/my_app/example.jpgIt is strongly recommended to namespace your files by putting them inside an extra subdirectory named after your app. This prevents Django from getting confused if two different apps happen to have a static file with the exact same name.
Django locates these files using finders, which are listed in the STATICFILES_FINDERS setting. By default it uses django.contrib.staticfiles.finders.FileSystemFinder (which searches STATICFILES_DIRS) followed by django.contrib.staticfiles.finders.AppDirectoriesFinder (which searches each app's static/ directory). The first match wins, which is exactly why namespacing matters.
When a file is not loading, you can ask Django where it is looking:
python manage.py findstatic my_app/example.jpg --verbosity 2Global Static Files
If you have project-level static assets that do not belong to a specific app, you can tell Django where to find them by defining the STATICFILES_DIRS setting:
STATICFILES_DIRS = [
BASE_DIR / "static",
# Optional additional paths
# "/var/www/static/",
]WARNING
STATIC_ROOT must not be one of the paths listed in STATICFILES_DIRS. Django raises an ImproperlyConfigured error, because collecting files into a directory it also collects from would copy them onto themselves.
Static File Storage
The STORAGES setting controls how files are written and how their URLs are generated. The staticfiles key covers your static assets, while the default key covers user-uploaded media.
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
},
}For production, you should swap the staticfiles backend for ManifestStaticFilesStorage. During collectstatic it appends a hash of each file's contents to its name (example.jpg becomes example.4b1a2c3d.jpg) and rewrites the references inside your CSS files to match. Because the name changes whenever the content changes, you can cache these files indefinitely without users ever seeing a stale copy.
STORAGES = {
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
},
}The static template tag automatically resolves to the hashed name, so your templates need no changes.
Configuring Media Files
Media files are not handled by the staticfiles app. You configure them with two settings instead. MEDIA_ROOT is the absolute filesystem path where uploads are saved, and MEDIA_URL is the public URL prefix they are served under.
MEDIA_URL = "media/"
MEDIA_ROOT = BASE_DIR / "media"NOTE
MEDIA_URL must be different from STATIC_URL, otherwise the two will collide.
Where each uploaded file lands inside MEDIA_ROOT is controlled by the upload_to argument on your model's FileField. The full upload workflow is covered in Working with Files.
Serving Static Files During Development
When you use django.contrib.staticfiles and have DEBUG = True in your settings, the built-in runserver command will automatically discover and serve static files from:
- Each app's
static/directory. - Any directories listed in
STATICFILES_DIRS.
This automatic method is inefficient and insecure. It is only meant for local development and should never be used in a live production environment.
Note that runserver does not serve files from the STATIC_ROOT directory, which is intended for production use and is populated by the collectstatic command. Django provides a helper function (static()) for this purpose and it can be used when:
- you are not using the
django.contrib.staticfilesapp, or - you want to serve files directly from
STATIC_ROOTto better imitate production behavior.
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# ... the rest of your URL configuration ...
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)NOTE
This helper function only works when DEBUG = True and only for local URL prefixes such as /static/ or /media/. It is intended for development use only and is not suitable for production.
If you need to test with DEBUG = False locally, runserver --insecure forces static files to be served anyway. As the name suggests, never use this flag on a live server.
Serving User-Uploaded Media Files During Development
You can use the exact same helper function to serve user-uploaded media files from your MEDIA_ROOT folder while developing locally. Once again, this is not suitable for production use.
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# ... the rest of your URL configuration ...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)Testing
When you write tests that rely on actual HTTP requests, your static assets need to load correctly so the test environment accurately reflects reality. The standard testing client assumes static files have already been collected into your STATIC_ROOT folder.
To avoid running the collection command before every single test, Django provides django.contrib.staticfiles.testing.StaticLiveServerTestCase. This class transparently serves all your static assets during test execution exactly like the development server does.
Deployment
Django provides a management command to gather all your static files into a single directory so they are ready for deployment.
First, you set the STATIC_ROOT variable in your settings file. This defines the absolute path on your server where you want the files to be collected.
STATIC_ROOT = "/var/www/example.com/static/"Next, you run the collectstatic command in your terminal.
python manage.py collectstaticThis command safely copies every static file from your various app folders and global directories (defined in STATICFILES_DIRS) into the designated STATIC_ROOT directory. Your web server (Nginx, Apache, CDN etc.) should then be configured to serve files directly from this directory.
A few flags matter in practice. --noinput skips the overwrite confirmation, which you need in any automated deploy script. --clear wipes STATIC_ROOT first so deleted assets do not linger. --dry-run shows what would be copied without touching anything.
python manage.py collectstatic --noinput --clearServing these files from a dedicated server, a cloud provider or a CDN is discussed in detail in Static & Media files in Production.
