Back to Blog

Django Static Files Not Loading in Production: The Five Usual Causes

admin
August 7, 2026 3 min read
29 views
The site works locally and arrives in production without CSS. Why DEBUG changes everything about static files, and the five reasons they stop being served.

Django Static Files Not Loading in Production: The Five Usual Causes

Locally it is perfect. In production the page arrives with no styling, the console shows a row of
404s, and nothing in your code changed. This is the single most common "it worked on my machine"
moment in Django deployment, and it happens because static files work fundamentally differently once
DEBUG is off.

Why turning off DEBUG breaks them

With DEBUG = True, Django's development server finds and serves static files for you, searching
your apps automatically. It is a convenience for development and explicitly not for production.

With DEBUG = False, Django serves nothing. Files must be collected into one directory and served by
something built for the job — your web server, a middleware designed for it, or a CDN. If you have
not arranged that, every static request returns 404. The application is fine; nothing is delivering
the assets.

Cause 1: collectstatic was never run

python manage.py collectstatic --noinput

This copies every static file from every app into STATIC_ROOT. It must run as part of every
deployment, after any change to assets. Forgetting it in a new pipeline is the most common cause of
all, and the giveaway is that the directory is empty or stale.

Cause 2: STATIC_ROOT and STATIC_URL are confused

They do different things and the names invite mixing them up.

STATIC_URL = "/static/"                   # the URL prefix browsers request
STATIC_ROOT = BASE_DIR / "staticfiles"    # the directory collectstatic writes to
STATICFILES_DIRS = [BASE_DIR / "assets"]  # extra places to collect from

STATIC_ROOT must not be one of STATICFILES_DIRS — collecting a directory into itself produces
confusing results and, on some versions, an error.

Cause 3: the web server points at the wrong directory

Your web server needs a rule mapping the URL prefix to the collected directory, and the path must
match exactly:

location /static/ {
    alias /srv/django-app/staticfiles/;
    expires 30d;
}

Note alias rather than root: with root the location is appended to the path, which produces a
directory that does not exist. It is a small distinction that costs people an hour.

Cause 4: permissions

The web server runs as its own account and must be able to read the files — and traverse every
directory above them. A collected directory inside a home directory with restrictive permissions
produces 403s rather than 404s, which at least tells you the path is right.

sudo -u www-data test -r /srv/django-app/staticfiles/css/app.css && echo readable

Running the check as the actual account is worth more than reading the permission bits.

Cause 5: the manifest cannot find a file

If you use a manifest-based storage backend, filenames get a content hash and a manifest maps the
original names to the hashed ones. If a referenced file is missing at collect time, the whole thing
fails:

ValueError: The file 'css/app.css' could not be found with ManifestStaticFilesStorage

Usually a template references a file that no longer exists, or a CSS file imports something outside
the collected tree. The error names the file; the fix is to correct the reference or add the missing
asset.

Media files are a different problem

Static files ship with your code. Media files are uploaded by users, live in MEDIA_ROOT, and need
their own serving rule. If images uploaded through the admin are missing while your CSS loads
correctly, you are looking at media configuration, not static.

A quick check after every deploy

curl -s -o /dev/null -w "%{http_code}\n" https://example.com/static/css/app.css

Anything other than 200 tells you now, rather than after a customer mentions the site looks broken.


If you are serving several applications from one machine, static files need a little more thought —
see Hosting Multiple Django Apps on One
Server
.

Comments (0)

Please login to leave a comment.

No comments yet. Be the first to comment!