Django has a well-earned reputation for being secure. It was built by journalists who wanted to build web applications without worrying about common security vulnerabilities, and many protections are built into the framework itself.
But "secure by default" doesn't mean "secure without configuration." Django gives you the tools, but you have to use them correctly. A misconfigured Django application can be just as vulnerable as any other web framework.
Before you deploy to production, run through this checklist. These are the 10 most critical security configurations for a Django application, ordered roughly by impact.
1. Set DEBUG = False
This is the single most important setting to change for production. When DEBUG is True, Django displays detailed error pages that include:
- Your complete settings, including the SECRET_KEY - The full source code of the files involved in the error - Database queries that were executed - Environment variables and their values - The complete stack trace with local variables
This is incredibly useful during development and catastrophically dangerous in production. Anyone who triggers an error in your application gets a complete map of your codebase, your configuration, and your secrets.
python DEBUG = False
Set this in your production settings file. Use environment variables to manage the difference between development and production:
python DEBUG = os.environ.get('DEBUG', 'False') == 'True'
## 2. Generate and Secure Your SECRET_KEY
We've covered this in detail in our Django SECRET_KEY guide, but it bears repeating because it's so critical.
Your SECRET_KEY must be: - At least 50 characters long - Generated by a cryptographically secure random number generator - Unique to this environment - Stored in an environment variable, never in source code - Never committed to version control
python SECRET_KEY = os.environ.get('SECRET_KEY')
If you're not sure whether your key is strong enough, generate a new one using our Django Key Generator and rotate it immediately.
3. Configure ALLOWED_HOSTS
When DEBUG is False, Django requires ALLOWED_HOSTS to be set. This prevents HTTP Host header attacks, where an attacker sends a request with a crafted Host header to make your application generate URLs pointing to a malicious site.
python ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']
Or from an environment variable:
python ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')
Never use ['*'] in production. It defeats the purpose of the setting entirely.
4. Enable HTTPS and Secure Cookies
If your site is served over HTTPS (and it should be), you need to tell Django to enforce secure connections and protect your cookies.
python SECURE_SSL_REDIRECT = True SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True SECURE_HSTS_SECONDS = 31536000 SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_HSTS_PRELOAD = True
These settings ensure: - HTTP requests are redirected to HTTPS - Session and CSRF cookies are only sent over HTTPS - HSTS tells browsers to always use HTTPS for your domain
5. Enable Security Middleware
Django's SecurityMiddleware provides several important protections. Make sure it's first in your MIDDLEWARE list:
python MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', # ... other middleware ]
This middleware handles SSL redirects, HSTS, content type sniffing prevention, andreferrer policy. Without it, several of the SECURE_ settings won't take effect.
Also ensure these are in your middleware:
python 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
## 6. Secure Your Database Configuration
Never put database credentials in your settings file. Load them from environment variables:
python DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('DB_NAME'), 'USER': os.environ.get('DB_USER'), 'PASSWORD': os.environ.get('DB_PASSWORD'), 'HOST': os.environ.get('DB_HOST'), 'PORT': os.environ.get('DB_PORT', '5432'), } }
For more on this pattern, read our guide on storing secret keys in environment variables.
7. Configure CORS Properly
If your API is consumed by a frontend on a different domain, you need CORS (Cross-Origin Resource Sharing) configured. Use django-cors-headers and be specific about which origins are allowed:
python CORS_ALLOWED_ORIGINS = [ 'https://yourfrontend.com', 'https://www.yourfrontend.com', ]
Never use CORS_ALLOW_ALL_ORIGINS = True in production. It allows any website to make requests to your API, which can be exploited for data theft and CSRF-like attacks.
8. Use Strong Password Hashing
Django uses PBKDF2 by default, which is good. But you can make it stronger by increasing the iteration count:
python PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', ]
Argon2 is the winner of the Password Hashing Competition and is generally recommended. You'll need to install the argon2 package: pip install django[argon2]
9. Validate File Uploads
File uploads are a common attack vector. Attackers can upload malicious files that exploit your server or other users. Django provides tools to validate uploads, but you have to use them.
Validate file types by extension and content type:
python from django.core.validators import FileExtensionValidator
class DocumentUploadForm(forms.Form): document = forms.FileField( validators=[FileExtensionValidator(['pdf', 'doc', 'docx'])] )
Set maximum upload size:
python DATA_UPLOAD_MAX_MEMORY_SIZE = 5242880 # 5 MB FILE_UPLOAD_MAX_MEMORY_SIZE = 5242880 # 5 MB
Never trust the content type from the browser. Validate the file content server-side if possible.
10. Run the Deployment Check
Django includes a built-in check command that scans for common production security issues:
bash python manage.py check --deploy
This checks for: - DEBUG setting - SECRET_KEY strength - ALLOWED_HOSTS configuration - HTTPS settings - Security middleware - Database connection security - And more
Run this before every deployment. Address every warning it raises. It's not exhaustive, but it catches the most common issues.
Bonus: Additional Security Measures
Beyond the core 10, consider these additional hardening steps:
**Rate limiting.** Use django-ratelimit or a reverse proxy like nginx to limit request rates. This prevents brute force attacks on login forms and API endpoints.
**Content Security Policy.** Set a CSP header to prevent XSS attacks by controlling which sources can load scripts, styles, and other resources. Use django-csp to manage this.
**Security headers.** Beyond what SecurityMiddleware provides, consider adding X-Content-Type-Options, Referrer-Policy, and Permissions-Policy headers.
**Logging and monitoring.** Log security-relevant events: failed login attempts, permission changes, admin actions. Monitor these logs for suspicious activity.
**Regular dependency updates.** Keep Django and all packages updated. Security vulnerabilities are discovered regularly, and updates patch them. Use tools like pip-audit or safety to check for known vulnerabilities.
**Two-factor authentication.** For admin access, use django-otp or django-two-factor-auth to require 2FA for admin logins. Your admin accounts are the most valuable targets.
The Verification Process
Don't just configure these settings once and forget about them. Security is an ongoing process.
**Before deployment:** Run manage.py check --deploy and fix every issue. Manually verify each item on this checklist.
**After deployment:** Test your production site with tools like securityheaders.com, SSL Labs, and Mozilla Observatory. These scan your site for common security misconfigurations.
**Regularly:** Re-run the deployment check periodically. Review your security settings as part of any infrastructure change. Keep dependencies updated.
**After incidents:** If you experience a security incident or suspect one, go through this checklist again. Rotate your SECRET_KEY, review your logs, and verify nothing has been tampered with.
The Bottom Line
Django gives you excellent security tools, but only you can ensure they're properly configured. Going through this checklist before every deployment catches the most common and most dangerous misconfigurations.
Security isn't a one-time setup. It's a continuous process of verification, improvement, and vigilance. But it starts with the basics: DEBUG=False, a strong SECRET_KEY, HTTPS, and the right middleware.
Run the checklist. Fix the issues. Sleep better knowing your Django app is as secure as it should be.
For the cryptographic foundation of all of this, make sure your SECRET_KEY is properly generated. Use our Django Key Generator and read our SECRET_KEY explained guide for the full story.