Guides / Django Server Error (500) only when DEBUG = False
Django Server Error (500) only when DEBUG = False
Everything works with DEBUG = True. Switch it off and every page returns Server Error (500) with no traceback anywhere. That is the hardest kind of error: the real exception is hidden on purpose, and Django's default logging doesn't print it either. Fix the logging first, then read the actual error.
What it means
With DEBUG = True, Django shows the exception page. With DEBUG = False it shows a bare 500 page (or your own 500.html) so visitors never see your code. The traceback only goes to Django's loggers, and by default the django logger writes to the console only when DEBUG is true. In production it emails ADMINS, which does nothing if you have not set up email. So the error happened, and nobody was told.
Step 1: make the error visible
Send errors to standard output, where every host, container and log viewer can see them:
# settings.py
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {"console": {"class": "logging.StreamHandler"}},
"root": {"handlers": ["console"], "level": "WARNING"},
"loggers": {
"django": {"handlers": ["console"], "level": "INFO", "propagate": False},
},
}
Deploy that, trigger the 500 again, and the traceback is in your logs. If you can't redeploy, reproduce it locally with the production settings and DEBUG = False: python manage.py runserver --insecure (the --insecure flag lets runserver serve static files with debug off).
Step 2: the usual causes
These are the errors that most often turn out to be behind the 500, most common first.
ValueError: Missing staticfiles manifest entry for '...'A template uses{% static %}for a file thatcollectstaticnever collected, and you use a manifest storage. Runcollectstaticbefore the server starts. More in the static files guide.- The database is not migrated.
relation "..." does not existorno such table. Runpython manage.py migrate(unapplied migrations). - A setting is empty because an environment variable is missing.
SECRET_KEY,DATABASES, an API key. The traceback names the setting. - A template error that debug mode hid.
TemplateDoesNotExistwhen the templates folder is not in the deployed image, or an exception inside a custom template tag. - Your own 500 handler is broken. A
500.htmlthat extends a base template using the request context fails too, and you get the bare page. Keep500.htmlstatic and self-contained.
A 400 Bad Request is a different problem (usually ALLOWED_HOSTS): DisallowedHost: Invalid HTTP_HOST header. A 403 on a form is CSRF verification failed.
Making it stick in production
- Keep the logging config above permanently, and add an error tracker such as Sentry so exceptions reach you without anyone reading logs.
- Run
python manage.py check --deployin CI. It catches the settings mistakes that only matter withDEBUG = False(deployment checklist). - Run the test suite once with
DEBUG = Falseand the production static storage, so manifest and template errors fail in CI and not in front of users.
Or skip it
Django Cloud collects static files on every build and adds your app's addresses to ALLOWED_HOSTS, two common causes of this. Your app's logs are searchable in the dashboard, or with djangocloud logs, and are kept for 30 days.
pip install djangocloud-cli
djangocloud deploy
Free plans are available for students, AWS Activate startups, open-source, non-profit and research projects. See how Django Cloud compares with other ways to host Django.
Django is a registered trademark of the Django Software Foundation. Django Cloud is not affiliated with or endorsed by it. Found a mistake in this guide? Tell us and we will fix it.