Production settings and the deploy checklist
Development and production are different worlds, and the settings that are fine on your laptop are dangerous on a public server. Deploying Django safely is mostly about flipping a specific set of settings and running one command that checks you did. This lesson is the production-settings checklist — what must change, why, and how to let Django audit it for you — so that going live is a deliberate, verified step rather than a hopeful one.
The mindset: production is hostile
On your laptop, you are the only user, on a trusted network, and errors help you. In production, anyone on the internet can send requests, mistakes leak information to attackers, and an error page must never expose your internals. So the production settings are not "nice to have" — several of them are the difference between a secure app and a breach. The good news: it is a known, finite checklist, and Django will run most of it for you.
The non-negotiables
These must be set correctly for any production Django site:
DEBUG = False # never True in production
ALLOWED_HOSTS = ["nidaan.example.com"] # your real domain(s)
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"] # from the environment, unique, secret
DEBUG = False— the single most important. WithDEBUG = True, an unhandled error shows a page containing your source code, settings and often environment variables — a complete map for an attacker. This is the most common and most dangerous Django misconfiguration.ALLOWED_HOSTS— the domains you serve; required onceDEBUG=False, and a defence against Host-header attacks. Empty here means Django rejects every request.SECRET_KEY— unique, secret, from the environment (never thedjango-insecure-…dev key, never committed). It signs sessions and tokens; a leak is a breach.
The HTTPS and cookie settings
Once your site is behind HTTPS (which every production site must be — the deploying lesson covers obtaining a certificate), tell Django to enforce it:
SECURE_SSL_REDIRECT = True # redirect http:// to https://
SESSION_COOKIE_SECURE = True # send the session cookie only over HTTPS
CSRF_COOKIE_SECURE = True # send the CSRF cookie only over HTTPS
SECURE_HSTS_SECONDS = 31536000 # HSTS: tell browsers to always use HTTPS (1 year)
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
Without these, a session or CSRF cookie can be sent over plain HTTP and intercepted, and users can reach the
insecure http:// version. SECURE_HSTS_SECONDS instructs browsers to refuse http:// for your domain for
that long — powerful, but commit to HTTPS before enabling it (it is sticky). These settings turn "we have a
certificate" into "we actually use it, everywhere".
Let Django check for you: check --deploy
You do not have to remember this list — Django audits it. Run, against your production settings:
python manage.py check --deploy
Verified: on a project with development settings this reports a series of security warnings, each with a
code — for example W018 (DEBUG is True), W020 (ALLOWED_HOSTS empty), W009 (SECRET_KEY
weak), W008 (SECURE_SSL_REDIRECT not set), W012/W016 (insecure session/CSRF cookies), and W004
(SECURE_HSTS_SECONDS not set). Each warning names exactly the setting to fix. Clear every warning before
you go live, and run this command as part of your deploy process so a regression is caught automatically.
It is the highest-value five seconds in the whole deployment — the checklist, run by the tool that knows it
best.
Databases, email and logging for production
Beyond security, three services switch from dev conveniences to real ones:
- Database — SQLite (the dev default) is a file and does not suit a concurrent production web app; you
move to PostgreSQL (the next lesson). The connection comes from the environment (
DATABASE_URL). - Email — the console backend (which prints emails) is replaced by a real SMTP or transactional-email service, so password resets and notifications actually send.
- Logging — configured to write
INFO/ERRORto files or a service (the errors-and-logging lesson), because withDEBUG=Falsethe traceback goes to logs, not the screen. If logging is not set up, a production error goes nowhere and you are blind.
All three follow the settings-and-secrets pattern: the code is the same, the configuration comes from the environment, and production supplies production values.
The pattern: one codebase, environment-driven config
Pulling it together, production settings are not a separate, hand-maintained file of magic values — they are
the same settings reading production values from the environment, with the security flags on. Whether you
use one env-driven settings.py or a split settings/production.py (the settings-and-secrets lesson), the
principles hold: DEBUG=False, real ALLOWED_HOSTS, secrets and database from the environment, HTTPS and
cookie security on, real email and logging — and check --deploy clean. Treat that as the pre-flight
checklist, every deploy, and going live stops being the scary part.
Check your work
The mindset. Production is hostile — anyone can send requests and mistakes leak information; the production settings are a known, finite, security-critical checklist.
The non-negotiables. DEBUG = False (most important — True leaks everything), real ALLOWED_HOSTS,
and a unique secret SECRET_KEY from the environment.
HTTPS/cookies. SECURE_SSL_REDIRECT, SESSION_COOKIE_SECURE, CSRF_COOKIE_SECURE, SECURE_HSTS_SECONDS
— enforce HTTPS everywhere once you have a certificate.
check --deploy. Audits the settings and warns about each unsafe one (verified: W018 DEBUG, W020
ALLOWED_HOSTS, W009 SECRET_KEY, W008 SSL redirect, W012/W016 cookies, W004 HSTS); clear all before going live
and run it every deploy.
Production services. PostgreSQL (not SQLite), a real email backend (not console), and configured logging
(since tracebacks go to logs with DEBUG=False) — all from the environment.
The pattern. One codebase, environment-driven config, security flags on, check --deploy clean.
Practice
- Run
python manage.py check --deployon the dev project and read every warning; for each code, name the setting it wants and the risk. - Set
DEBUG=Falsewith a realALLOWED_HOSTSfrom the environment; trigger an error and confirm users get a generic 500, not a traceback. - Add the HTTPS/cookie settings and re-run
check --deploy; confirm those specific warnings clear. - Move
SECRET_KEYand the database config to environment variables; confirm the app runs with them set. - Configure logging so a production error is written to a file; trigger one and find it in the log.
- Write your own pre-deploy checklist for Nidaan, ending with "
check --deployis clean".
Official documentation
- Django — Deployment checklist — Every production setting, with the warning codes.
- Django —
check --deploy— The audit command. - Django — Security — Why each setting matters.
Next: moving from SQLite to PostgreSQL.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship