Cryptography

Django Security Checklist: 10 Things to Check Before Production

12 min read
By
Django Security Checklist: 10 Things to Check Before Production

Photo by Markus Spiske from Pexels

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.

Frequently Asked Questions

Is Django secure by default?

Django has strong security defaults, but it is not secure by default in an absolute sense. Many security features require proper configuration, such as setting a strong SECRET_KEY, enabling HTTPS, configuring ALLOWED_HOSTS, and setting DEBUG to False. You must actively verify and configure these settings before production deployment.

What is the most common Django security mistake?

The most common mistake is deploying with DEBUG=True still enabled. This exposes sensitive information including source code, environment variables, database queries, and the SECRET_KEY in error pages. Always set DEBUG=False in production.

Should I use Django built-in security middleware?

Yes. Django includes several security middleware classes that should be enabled in production: SecurityMiddleware, SessionMiddleware, CommonMiddleware, CsrfViewMiddleware, XFrameOptionsMiddleware, and ClickjackingMiddleware. These provide essential protections with minimal configuration.

How do I check if my Django app is secure?

Use the built-in manage.py check --deploy command, which checks for common production security issues. Additionally, review your settings against this checklist, run security scanners, perform penetration testing, and consider using tools like the Django Security Audit or third-party security scanners.

Try NovelCrypt Tools

Experience military-grade encryption for your sensitive data. Create self-destructing messages, encrypt files, or explore our experimental lab tools.

Explore NovelCrypt