Skip to content

Settings & Production#

Complete guide to configuring Django settings.py for production and best practices.

🔒 Security Settings#

Secret Key#

# myproject/settings.py
from decouple import config

# NEVER commit secret key to version control
SECRET_KEY = config('SECRET_KEY')

# .env file
# SECRET_KEY=your-super-secret-key-here

Debug Mode#

# Development
DEBUG = True

# Production - ALWAYS False
DEBUG = False

# Or use environment variable
DEBUG = config('DEBUG', default=False, cast=bool)

Allowed Hosts#

# Development
ALLOWED_HOSTS = []

# Production
ALLOWED_HOSTS = [
    'example.com',
    'www.example.com',
    'api.example.com',
]

# Or from environment
ALLOWED_HOSTS = config(
    'ALLOWED_HOSTS',
    default='',
    cast=lambda v: [s.strip() for s in v.split(',') if s.strip()]
)

Security Middleware#

# myproject/settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',  # Must be first
    # ... other middleware
]

# Security settings
SECURE_SSL_REDIRECT = True  # Redirect HTTP to HTTPS
SESSION_COOKIE_SECURE = True  # Only send cookies over HTTPS
CSRF_COOKIE_SECURE = True  # Only send CSRF cookie over HTTPS
SECURE_HSTS_SECONDS = 31536000  # HTTP Strict Transport Security
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'  # Prevent clickjacking

🗄️ Database Configuration#

PostgreSQL (Production)#

# myproject/settings.py
from decouple import config

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': config('DB_NAME'),
        'USER': config('DB_USER'),
        'PASSWORD': config('DB_PASSWORD'),
        'HOST': config('DB_HOST', default='localhost'),
        'PORT': config('DB_PORT', default='5432'),
        'OPTIONS': {
            'connect_timeout': 10,
        },
        'CONN_MAX_AGE': 600,  # Persistent connections
    }
}

Connection Pooling#

# For connection pooling (PgBouncer, etc.)
DATABASES = {
    'default': {
        # ... database config
        'CONN_MAX_AGE': 0,  # Disable persistent connections
    }
}

📧 Email Configuration#

SMTP Settings#

# myproject/settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = config('EMAIL_HOST', default='smtp.gmail.com')
EMAIL_PORT = config('EMAIL_PORT', default=587, cast=int)
EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=True, cast=bool)
EMAIL_HOST_USER = config('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD')
DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL', default=EMAIL_HOST_USER)

Console Backend (Development)#

# Development - emails printed to console
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

File Backend (Testing)#

# Testing - emails saved to files
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = BASE_DIR / 'emails'

📁 Static & Media Files#

Static Files#

# myproject/settings.py
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'

# Collect static files before deployment
# python manage.py collectstatic

# Use WhiteNoise for serving
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

Media Files#

# Development
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

# Production - Use cloud storage (see static-media.md)
# Or serve with nginx

🎯 Logging Configuration#

Production Logging#

# myproject/settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {message}',
            'style': '{',
        },
        'simple': {
            'format': '{levelname} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'file': {
            'level': 'INFO',
            'class': 'logging.FileHandler',
            'filename': BASE_DIR / 'logs' / 'django.log',
            'formatter': 'verbose',
        },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple',
        },
    },
    'root': {
        'handlers': ['console', 'file'],
        'level': 'INFO',
    },
    'loggers': {
        'django': {
            'handlers': ['console', 'file'],
            'level': 'INFO',
            'propagate': False,
        },
        'myapp': {
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': False,
        },
    },
}

⚡ Performance Settings#

Caching#

# myproject/settings.py

# Memory cache (development)
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
    }
}

# Redis cache (production)
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': config('REDIS_URL', default='redis://127.0.0.1:6379/1'),
    }
}

# Cache timeout
CACHE_MIDDLEWARE_SECONDS = 600

Database Connection Pooling#

# Use persistent connections
DATABASES = {
    'default': {
        # ... database config
        'CONN_MAX_AGE': 600,  # 10 minutes
    }
}

🔐 Session Configuration#

# myproject/settings.py

# Session settings
SESSION_ENGINE = 'django.contrib.sessions.backends.db'  # Database (default)
# Or use cache
# SESSION_ENGINE = 'django.contrib.sessions.backends.cache'

SESSION_COOKIE_AGE = 86400  # 24 hours
SESSION_COOKIE_SECURE = True  # HTTPS only (production)
SESSION_COOKIE_HTTPONLY = True  # Prevent JavaScript access
SESSION_COOKIE_SAMESITE = 'Lax'  # CSRF protection

🌍 Internationalization#

# myproject/settings.py

LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True

# Supported languages
LANGUAGES = [
    ('en', 'English'),
    ('es', 'Spanish'),
    ('fr', 'French'),
]

# Locale paths
LOCALE_PATHS = [
    BASE_DIR / 'locale',
]

📦 Installed Apps#

# myproject/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # Third-party apps
    'whitenoise.runserver_nostatic',  # If using WhiteNoise

    # Your apps
    'myapp',
]

🛡️ Middleware#

# myproject/settings.py

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  # If using WhiteNoise
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

🔧 Environment-Based Settings#

Split Settings Files#

# myproject/settings/
# ├── __init__.py
# ├── base.py
# ├── development.py
# └── production.py
# myproject/settings/base.py
from pathlib import Path
from decouple import config

BASE_DIR = Path(__file__).resolve().parent.parent.parent

SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='', cast=lambda v: [s.strip() for s in v.split(',') if s.strip()])

INSTALLED_APPS = [
    # ... apps
]

MIDDLEWARE = [
    # ... middleware
]

ROOT_URLCONF = 'myproject.urls'
WSGI_APPLICATION = 'myproject.wsgi.application'

# ... common settings
# myproject/settings/development.py
from .base import *

DEBUG = True
ALLOWED_HOSTS = ['localhost', '127.0.0.1']

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# Development-specific settings
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# myproject/settings/production.py
from .base import *

DEBUG = False
ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=lambda v: [s.strip() for s in v.split(',')])

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': config('DB_NAME'),
        'USER': config('DB_USER'),
        'PASSWORD': config('DB_PASSWORD'),
        'HOST': config('DB_HOST'),
        'PORT': config('DB_PORT', default='5432'),
    }
}

# Production-specific settings
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
# myproject/settings/__init__.py
from decouple import config

ENVIRONMENT = config('ENVIRONMENT', default='development')

if ENVIRONMENT == 'production':
    from .production import *
else:
    from .development import *

✅ Production Checklist#

Before Deployment#

  • [ ] Set DEBUG = False
  • [ ] Set ALLOWED_HOSTS with your domain
  • [ ] Use environment variables for secrets
  • [ ] Configure production database
  • [ ] Set up email backend
  • [ ] Configure static files (collectstatic)
  • [ ] Configure media files (cloud storage or nginx)
  • [ ] Set up logging
  • [ ] Enable security middleware settings
  • [ ] Use HTTPS (SSL certificates)
  • [ ] Set up monitoring/error tracking
  • [ ] Run python manage.py check --deploy
  • [ ] Test all functionality

Security Checklist#

  • [ ] SECRET_KEY in environment variable
  • [ ] DEBUG = False
  • [ ] ALLOWED_HOSTS configured
  • [ ] SECURE_SSL_REDIRECT = True
  • [ ] SESSION_COOKIE_SECURE = True
  • [ ] CSRF_COOKIE_SECURE = True
  • [ ] Strong database passwords
  • [ ] Regular security updates
  • [ ] Use HTTPS everywhere

🚀 Deployment Commands#

# Collect static files
python manage.py collectstatic --noinput

# Run migrations
python manage.py migrate

# Create superuser (if needed)
python manage.py createsuperuser

# Check deployment settings
python manage.py check --deploy

# Compile messages (if using i18n)
python manage.py compilemessages

✅ Best Practices#

  1. Use environment variables - Never hardcode secrets
  2. Split settings - Separate dev/prod settings
  3. Use python-decouple - For environment variables
  4. Enable security settings - In production
  5. Use HTTPS - Always in production
  6. Set up logging - Monitor your application
  7. Use connection pooling - For database
  8. Cache static files - Use WhiteNoise or CDN
  9. Monitor errors - Use Sentry or similar
  10. Regular backups - Database and media files

✅ Next Steps#


Pro Tip: Always use python manage.py check --deploy before deploying. It checks for common production issues. Never commit secrets to version control - use environment variables!