Static files

Django · Essentials · 8 min

Every site has assets, and Django's static pipeline handles them differently in development and production.

Static files are assets shipped with your code: CSS, JavaScript, images. The django.contrib.staticfiles app (installed by default) finds them in:

  • each app's static/ folder, namespaced like templates: blog/static/blog/site.css
  • extra directories listed in STATICFILES_DIRS, for project-wide assets

In templates, load the tag library and build URLs with {% static %}:

{% load static %}
<link rel="stylesheet" href="{% static 'blog/site.css' %}">

The URL is based on STATIC_URL (default "static/"). In development, runserver serves static files automatically when DEBUG=True.

In production, Django does not serve them. Run python manage.py collectstatic, which copies everything into STATIC_ROOT, and let something efficient serve that folder: Nginx, a CDN, or the WhiteNoise package inside your app.

Configure ManifestStaticFilesStorage (in STORAGES["staticfiles"]) to give files hashed names like site.3f2a1c.css, so browsers can cache them forever and still get new versions after a deploy. That only works if every reference uses {% static %}.

Media files are different: files uploaded by users. They use MEDIA_URL and MEDIA_ROOT (or a cloud storage backend) and must never be mixed with static files, because user uploads are untrusted.

Example

# mysite/settings.py
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent

STATIC_URL = "static/"
STATICFILES_DIRS = [BASE_DIR / "assets"]          # project-wide files
STATIC_ROOT = BASE_DIR / "staticfiles"            # collectstatic target
STORAGES = {
    "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
    "staticfiles": {"BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"},
}
MEDIA_URL = "media/"                              # user uploads, kept separate
MEDIA_ROOT = BASE_DIR / "media"

# template:
#   {% load static %}
#   <img src="{% static 'blog/logo.png' %}" alt="logo">
# deploy:
#   python manage.py collectstatic --noinput

Remember

  • App assets live in app/static/app/; link them with {% static %}
  • collectstatic gathers files into STATIC_ROOT for the web server or CDN
  • Media (user uploads) are separate from static files

Watch out for

  • Hard-coding /static/... paths, which breaks hashed filenames and CDN URLs.

Check yourself

  1. What does python manage.py collectstatic do?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.djangoproject.com/en/6.0/howto/static-files/