Django’s default settings are tuned for development. Running them in production leaks debug information, accepts any host, and misses important security headers. This checklist covers the settings that matter, and Django’s own check --deploy command catches most mistakes.

Deployment guide: deploy Django with Gunicorn and Nginx.

1. Run the deployment check

DJANGO_SETTINGS_MODULE=app.settings.production python manage.py check --deploy

It warns about DEBUG, SECRET_KEY, HTTPS, cookies, HSTS and more. Fix everything it reports (or consciously accept it).

2. Configuration from the environment

# settings/production.py
import os
from .base import *

DEBUG = False
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",")
CSRF_TRUSTED_ORIGINS = ["https://example.com", "https://www.example.com"]
  • DEBUG=False in production, always. Debug pages expose settings, SQL and source code.
  • SECRET_KEY is long, random, and not in Git. Generate one with python -c "import secrets; print(secrets.token_urlsafe(50))".
  • ALLOWED_HOSTS lists only your domains.

Store values in an .env file (chmod 600), or systemd EnvironmentFile=. See twelve-factor app and secrets management.

3. HTTPS and cookies

Behind Nginx terminating TLS:

SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000          # start lower (e.g. 3600) and increase
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = "DENY"

Nginx must send X-Forwarded-Proto (proxy_set_header X-Forwarded-Proto $scheme;). Otherwise SECURE_SSL_REDIRECT causes redirect loops. Only trust that header when Django is reachable only through your proxy. See Let’s Encrypt.

4. Database

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": os.environ["DB_NAME"],
        "USER": os.environ["DB_USER"],
        "PASSWORD": os.environ["DB_PASSWORD"],
        "HOST": os.environ.get("DB_HOST", "127.0.0.1"),
        "CONN_MAX_AGE": 60,                 # reuse connections
        "CONN_HEALTH_CHECKS": True,
    }
}

PostgreSQL is the most popular choice for Django. For many workers, add PgBouncer, or Django’s built-in connection pooling (Django 5.1+ with psycopg). See PostgreSQL performance tuning.

5. Caching and sessions

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": os.environ.get("REDIS_URL", "redis://127.0.0.1:6379/1"),
    }
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"   # or cached_db

See install Redis.

6. Static and media files

STATIC_ROOT, collectstatic on deploy, and serving via Nginx or WhiteNoise. See Django static files.

7. Logging

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {"console": {"class": "logging.StreamHandler"}},
    "root": {"handlers": ["console"], "level": "INFO"},
    "loggers": {"django.request": {"handlers": ["console"], "level": "ERROR", "propagate": False}},
}

Log to stdout, and let systemd/journald or Docker collect it. Add an error tracker (Sentry or GlitchTip) for exceptions, with context. See log management and error tracking with Sentry.

8. Email

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = os.environ["EMAIL_HOST"]
EMAIL_PORT = 587
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = "[email protected]"
SERVER_EMAIL = "[email protected]"
ADMINS = [("Ops", "[email protected]")]

See transactional email services.

9. Application server and workers

  • Gunicorn with --workers ≈ 2×CPU+1 (sync), or Uvicorn workers for async views. See Gunicorn vs Uvicorn.
  • Background jobs in Celery, not in requests. See Celery workers.
  • Migrations run once per deploy (python manage.py migrate --noinput), not on every worker start.

10. Admin and accounts

  • Move the admin off /admin/ (a different URL path), and protect it with 2FA (django-otp), or an IP allowlist.
  • Use strong password validators (AUTH_PASSWORD_VALIDATORS).
  • Keep Django and dependencies updated (pip list --outdated, Dependabot). Security releases are frequent.

Quick reference

Setting Production value
DEBUG False
SECRET_KEY From environment
ALLOWED_HOSTS Your domains
HTTPS SECURE_SSL_REDIRECT, secure cookies, HSTS
Proxy SECURE_PROXY_SSL_HEADER
DB CONN_MAX_AGE, health checks
Check manage.py check --deploy

Related: deploy FastAPI with Uvicorn and Linux server hardening checklist.

  • Django
  • Python
  • Security