Home/Blog/Django Security Checklist: From Settings to Production
Software Development
Django Security Checklist: From Settings to Production
AJAjish StephenAugust 8, 20267 min read
Django's defaults are genuinely good — better than most frameworks out of the box. But "good defaults" doesn't mean "secure by accident." Most Django security incidents I've seen trace back to a handful of settings that were never revisited after local development. Here's the checklist I actually run before any Django project goes live.
Run the built-in deployment check first
Before touching anything manually, let Django tell you what it already knows is wrong:
pythonmanage.py check --deploy
This flags common issues like DEBUG=True, missing HSTS settings, and an insecure SECRET_KEY — treat every warning as something to actually fix, not just acknowledge.
The production settings block
A reasonable baseline for settings.py in production:
SESSION_COOKIE_SECURE and CSRF_COOKIE_SECURE ensure these cookies are only ever sent over HTTPS — without them, a session cookie could leak over an accidental plain HTTP connection.
SECRET_KEY doesn't belong in your codebase
SECRET_KEY signs sessions, password reset tokens, and CSRF tokens — if it leaks, all of that can be forged. Pull it from the environment instead of hardcoding it:
importos
SECRET_KEY= os.environ['DJANGO_SECRET_KEY']
Using os.environ[...] instead of os.environ.get(...) is deliberate — it raises a clear error at startup if the variable is missing, rather than silently falling back to an insecure default.
Stay inside the ORM for SQL safety
Django's ORM parameterizes queries automatically. The risk reappears the moment raw SQL is built with string formatting:
# ✗ Vulnerable — f-string interpolation into raw SQL User.objects.raw(f"SELECT * FROM users WHERE email = '{email}'")
# ✓ Safe — parameterized User.objects.raw("SELECT * FROM users WHERE email = %s", [email])
# ✓ Better — use the ORM directly User.objects.filter(email=email)
A pre-launch checklist worth keeping
→manage.py check --deploy returns no warnings
→DEBUG is False and confirmed by actually visiting a broken URL in the deployed environment
→ALLOWED_HOSTS is an explicit list, never a wildcard
→Dependencies checked for known CVEs before every deploy, ideally as part of CI
Want a real security review of your Django application before it goes live, or after it's already been running for a while? This is part of what I cover in DevOps services engagements alongside infrastructure hardening.
Common questions
What does Django's check --deploy command actually do?
It scans your settings against a known list of production security concerns and warns about issues like DEBUG being enabled or missing HSTS headers. It is not a full security audit, but it catches the most common configuration mistakes in seconds.
Is Django's ORM safe from SQL injection by default?
Yes, as long as you use the ORM's query methods rather than raw SQL. The moment you drop into raw() with string-formatted user input, that protection disappears.
Why does SECRET_KEY matter so much?
It signs session cookies, password reset tokens, and CSRF tokens. If it leaks, an attacker can forge valid signed data, including session cookies for arbitrary users.
Do I need DEBUG = False even on a small internal project?
Yes, always in production, regardless of project size. DEBUG = True exposes full stack traces and settings to anyone who triggers an error.
Is your Django app actually production-ready?
I review real Django applications for security gaps before and after launch.