Security hardening for Django in production
Headers, CSP, admin protection, brute-force defence, server hardening, and supply-chain hygiene.
Hardening closes the doors attackers try first: open ports, password SSH, missing security headers, an exposed admin, and outdated packages. None of it is exotic, and all of it is regularly found open.
The guides cover the server, the Django settings, headers and CSP, the admin, uploads, and the checklists that confirm it all stays closed.
Set it up
Guides that take this from nothing to running in production.
- Put the Django Admin on a Private Tailscale Network
Remove the Django admin from the public internet by serving it only over Tailscale: install Tailscale on the server, route /admin through a tailnet-only Nginx server block or a separate admin hostname, and keep staff access simple.
- Add Two-Factor Authentication to the Django Admin
Require two-factor authentication for the Django admin in production: django-otp with OTPAdminSite and TOTP devices, recovery codes, enrolling existing staff, passkeys with django-allauth MFA, and avoiding lockouts during rollout.
- Password-Protect a Django Staging Site
Keep a Django staging site private: HTTP basic auth in Nginx or Caddy with health-check and webhook exceptions, Django middleware for PaaS hosts, noindex headers, IP or VPN restrictions, and why basic auth interferes with API tokens.
- Protect Django Endpoints With mTLS Client Certificates in Nginx
Require mutual TLS client certificates for Django admin, internal APIs, or partner integrations with Nginx: create a private CA, issue client certificates, configure ssl_verify_client, pass verified identity to Django, and handle revocation and renewal.
- Connect Django Servers Over a WireGuard Private Network
Use WireGuard to connect Django app servers to PostgreSQL, Redis, and workers across hosts or providers: generate keys, configure wg0 interfaces, bind databases to the tunnel address, restrict firewalls, and keep Django connection settings on private IPs.
- Protect the Django Admin With Cloudflare Access
Put the Django admin behind Cloudflare Access (Zero Trust): create a self-hosted application for /admin, require SSO or email one-time codes, validate the Cf-Access-Jwt-Assertion header in Django, and block direct origin access so nobody can bypass it.
- Protect a Django Server With CrowdSec
Set up CrowdSec to protect a Django server: install the agent and Nginx or firewall bouncer, parse Nginx and Django login logs, detect brute force on /admin and login views, share blocklists, and avoid banning legitimate users behind proxies.
- Protect Django on AWS With AWS WAF
Put AWS WAF in front of a Django app on an ALB, CloudFront, or API Gateway: start with AWS managed rule groups in count mode, add rate-based rules for login and API endpoints, exclude rules that break uploads or admin forms, and log and tune blocked requests.
- Deploy Django on Rocky Linux with SELinux
Deploy Django with Gunicorn and Nginx on Rocky Linux, AlmaLinux, or RHEL with SELinux enforcing: firewalld, file contexts, booleans, and reading AVC denials.
- Handle Stripe Webhooks in Production Django
Handle Stripe webhooks reliably in a production Django app: verify signatures with the raw body, exempt only the webhook view from CSRF, respond fast and process in Celery, make handlers idempotent with event IDs, and monitor failed deliveries.
- Run a Django Docker Container as a Non-Root User
Run Django containers as a non-root user: create a dedicated user in the Dockerfile, fix ownership of app, static, and media paths, handle volume permissions, bind high ports, and add a read-only root filesystem and dropped capabilities.
- Build a Distroless Docker Image for Django
Build a minimal, shell-less production image for Django using a multi-stage build with a Debian builder and a distroless or Chainguard Python runtime: copy the virtualenv, run as non-root, handle collectstatic and migrations without a shell, and debug safely.
Run it well
Tuning, reliability, and operations once it works.
- Django Deployment Security Basics You Should Not Skip
A lot of Django apps reach production with local-development assumptions still in place: DEBUG=True, weak host validation, secrets in .env files with broad permissions, Gunicorn...
- Rotate the Django SECRET_KEY Safely
Rotate Django's SECRET_KEY without breaking sessions and password reset links: what the key signs, SECRET_KEY_FALLBACKS, a staged rollout, leak response, and third-party libraries that use the key.
- Configure a Content Security Policy for Django
Add a Content Security Policy to Django: Django 6 built-in CSP settings or django-csp, nonces for inline scripts, report-only rollout, collecting violation reports, and handling third-party scripts.
- Protect Django Logins with fail2ban
Block brute-force attacks on Django login and admin pages with fail2ban: log failed logins with the real client IP, write a filter and jail, ban at the firewall, and combine with app-level rate limiting.
- Enable Automatic Security Updates on a Django Server
Keep a Django server patched automatically: unattended-upgrades on Ubuntu and Debian or dnf-automatic on RHEL, security-only origins, service restarts, reboot windows, exclusions for PostgreSQL, and monitoring.
- Keep an Audit Log of Changes in Django
Record who changed what and when in a Django app: compare django-simple-history, django-auditlog, and pghistory triggers, capture the acting user and request ID, protect audit tables from tampering, plan retention, and keep audit writes fast.
- Protect Django From Bots and Scrapers With a WAF
Defend a Django site against abusive bots, scrapers, and credential stuffing with a web application firewall: Cloudflare or AWS WAF managed rules, rate-based rules, bot challenges on login and signup, protecting APIs, and avoiding false positives.
- Lock Out Brute-Force Logins With django-axes
Protect Django logins from password guessing with django-axes: install the backend and middleware, choose lockout parameters by user and IP, get real client IPs behind proxies, store attempts in cache or database, and unlock users safely.
- Stop Django Signup and Form Spam With Turnstile
Stop bot signups and contact-form spam in Django: add Cloudflare Turnstile with server-side verification, honeypot fields and timing checks, rate limits, email confirmation, and how to keep forms accessible and private.
- Switch Django to Argon2 Password Hashing
Upgrade Django password hashing to Argon2id: install argon2-cffi, put Argon2PasswordHasher first, let existing hashes upgrade on login, wrap old hashes to upgrade everyone now, tune memory and time cost, and size servers for login load.
- What to Do When Django Secrets Leak
Respond to a leaked Django SECRET_KEY, database password, or API key: assess exposure, rotate in the right order, use SECRET_KEY_FALLBACKS only when appropriate, invalidate sessions and tokens, purge Git history, and prevent the next leak.
- Rate Limit Django Views With django-ratelimit
Add application-level rate limiting to Django views with django-ratelimit: protect login, signup, password reset, and search, key limits by IP, user, or field, use a shared Redis cache, get the real client IP behind proxies, and return helpful 429 responses.
- Cloudflare WAF Rules for a Django Site
Configure Cloudflare WAF custom rules, rate limiting, and managed rulesets for Django: protect /admin and login views, challenge bad bots, skip rules for webhooks and uploads, avoid blocking legitimate API clients, and verify the real client IP at the origin.
- Add security.txt and a Vulnerability Disclosure Process to a Django Site
Publish /.well-known/security.txt from Django with contact, expiry, policy, and encryption fields, set up a vulnerability disclosure policy and intake mailbox, triage reports, and avoid common mistakes like expired files and unmonitored addresses.
- Rate Limit Django Endpoints with Nginx
Protect Django login, API, and expensive endpoints with Nginx limit_req: zones, burst and nodelay, 429 responses, real client IPs, and dry runs.
- Automate Django Dependency and Security Updates
Keep Django and its dependencies current safely: Dependabot or Renovate, pip-audit in CI, grouped updates, test gates, and urgent security releases.
- Copy Production Data to Staging Safely (Anonymised)
Give a Django staging environment realistic data without exposing customers: copy production, anonymise personal fields with a management command or tooling, subset large tables, disable outbound email and payments, and automate refreshes.
- Scan Django Docker Images for Vulnerabilities With Trivy
Scan Django container images with Trivy for vulnerable OS packages and Python dependencies: run it locally and in CI, fail builds on fixable critical issues, ignore false positives with expiry, rebuild on new base images, and scan running registries.
- Harden Nginx TLS for Django (SSL Labs A+)
Configure Nginx TLS for a Django site to earn an SSL Labs A+: TLS 1.2 and 1.3 only, modern ciphers, HSTS with Django settings, OCSP stapling, session settings, certificate chain, and verifying with SSL Labs and testssl.sh.
- Encrypt Django Database and Media Backups
Encrypt PostgreSQL dumps and media backups for a Django app: use age or GPG for client-side encryption, restic or pgBackRest built-in encryption, server-side encryption with KMS on S3, manage and store keys separately, and test restores with decryption.
- Restrict Django Pod Traffic With Kubernetes NetworkPolicies
Lock down network access for Django on Kubernetes: default-deny ingress and egress, allow the ingress controller to reach web pods, let web and Celery reach PostgreSQL, Redis, and DNS, permit required external APIs, and test policies without breaking the app.
Check it
Review lists to run before launch or after a change.
- Django Security Headers Checklist
Check every HTTP security header a Django site should send: HSTS, CSP, frame options, nosniff, referrer and permissions policies, COOP, and cookie flags.
- Django check --deploy Explained: Every Warning and Fix
Understand every warning from python manage.py check --deploy: HSTS, SSL redirect, cookies, SECRET_KEY, DEBUG, ALLOWED_HOSTS, and when silencing is justified.
- Linux Server Hardening Checklist for Django
Harden a Linux server running Django: SSH keys, firewall rules, automatic security updates, least-privilege users, systemd sandboxing, fail2ban, and audit checks.
- Django Admin Security Checklist
Secure the Django admin in production: access restrictions, strong authentication, permissions, session policy, audit logging, and safe data handling.
- Django Privacy and GDPR Production Checklist
A technical privacy checklist for Django in production: personal data inventory, logs and error tracking, cookies, retention and deletion including backups, exports, encryption, access control, and processors.
- Django SOC 2 Readiness Checklist
A technical readiness checklist for SOC 2 on a Django application: access control and MFA, change management through CI/CD, logging and monitoring, encryption, backups and recovery tests, vulnerability management, vendor review, and evidence collection.
- Django HIPAA Technical Safeguards Checklist
A technical checklist for running a Django application that handles protected health information under HIPAA: BAAs with vendors, access control and MFA, audit logging, encryption in transit and at rest, PHI-safe logging and error tracking, backups, and breach readiness.
- Django PCI DSS Checklist for Apps That Take Payments
A PCI DSS checklist for Django apps accepting card payments: minimize scope with hosted payment fields or redirects, never store card data, secure the pages that load payment scripts, protect against script tampering, manage access, logging, vulnerability scanning, and SAQ evidence.
- Django Penetration Test Preparation Checklist
Prepare a Django application for a penetration test: fix the findings testers always report first, agree scope and rules of engagement, set up a production-like test environment with seeded accounts, keep monitoring on, and plan remediation and retesting.
- Django Deployment Checklist for Production
A Django app that works locally is not automatically safe to run in production. Most deployment failures come from a small set of issues: wrong settings, missing secrets, unsafe...
- Django Production Settings Checklist (DEBUG, ALLOWED_HOSTS, CSRF)
A lot of Django production incidents come from settings.py, not application code.
- Django Dependency Security Checklist
Secure the software supply chain of a Django app: supported Django versions, pinned and hashed dependencies, vulnerability scanning, private index safety, container image updates, CI action pinning, and SBOMs.
- Django File Upload Security Checklist
Secure user uploads in Django production: size limits at every layer, content-based type checks, image re-encoding, safe file names, private storage, serving user content safely, scanning, and quotas.
- Django on AWS Production Checklist
A production checklist for Django on AWS: account security, VPC and security groups, IAM roles instead of keys, RDS and ElastiCache settings, S3 and CloudFront, secrets, TLS, logging, backups, and cost controls.
- Django SaaS Launch Checklist
A launch checklist for a Django SaaS product: production settings and security, tenant isolation, authentication and 2FA, billing webhooks, email deliverability, backups, monitoring, legal pages and data protection, support, and capacity.
- Django Multi-Tenant SaaS Production Checklist
A production checklist for multi-tenant Django SaaS apps: choose shared tables, schemas, or databases, enforce tenant isolation in queries and caches, scope files and background jobs, handle custom domains, run migrations per tenant, and test for cross-tenant leaks.
- Django Kubernetes Security Checklist
A security checklist for Django on Kubernetes: non-root containers with read-only filesystems, Pod Security Standards, minimal RBAC and service account tokens, secrets from a manager, NetworkPolicies, image scanning and signing, patched nodes, and audit logging.
Fix it
Errors and symptoms, each traced to a cause and a fix.
- Fix Django 403 Forbidden Errors in Production
Fix 403 Forbidden errors on a Django site in production: tell Nginx 403s from Django 403s, fix CSRF failures, file permissions on static and media, permission checks in views, and WAF or Cloudflare blocks.
- Fix "CSRF_TRUSTED_ORIGINS Must Start With a Scheme" in Django
Fix the Django 4.0+ system check error 4_0.E001, which says CSRF_TRUSTED_ORIGINS values must start with a scheme: write full origins with https://, use wildcard subdomains correctly, parse the setting from environment variables, and avoid trailing slashes.
Learning paths through this topic
- Your first production deployment on a VPSFrom a fresh Ubuntu server to a Django site on HTTPS, with the settings and checks that keep it safe.
- Docker, from image to productionBuild a production image, run it with Compose, ship it from CI, and check the result.
- Hardening a Django deploymentClose the common gaps in settings, headers, the admin, the server, and the software supply chain.
- Django on KubernetesRun Django on a cluster with config, ingress, probes, jobs, graceful shutdown, and autoscaling, then package it with Helm and GitOps.