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.

  1. ValueError: Missing staticfiles manifest entry for '...' A template uses {% static %} for a file that collectstatic never collected, and you use a manifest storage. Run collectstatic before the server starts. More in the static files guide.
  2. The database is not migrated. relation "..." does not exist or no such table. Run python manage.py migrate (unapplied migrations).
  3. A setting is empty because an environment variable is missing. SECRET_KEY, DATABASES, an API key. The traceback names the setting.
  4. A template error that debug mode hid. TemplateDoesNotExist when the templates folder is not in the deployed image, or an exception inside a custom template tag.
  5. Your own 500 handler is broken. A 500.html that extends a base template using the request context fails too, and you get the bare page. Keep 500.html static 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

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

Get startedSee pricing

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.