RizTech Academy logo
RizTech Academy
DeploymentLesson 1 of 530 min

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. With DEBUG = 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 once DEBUG=False, and a defence against Host-header attacks. Empty here means Django rejects every request.
  • SECRET_KEY — unique, secret, from the environment (never the django-insecure-… dev key, never committed). It signs sessions and tokens; a leak is a breach.

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/ERROR to files or a service (the errors-and-logging lesson), because with DEBUG=False the 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

  1. Run python manage.py check --deploy on the dev project and read every warning; for each code, name the setting it wants and the risk.
  2. Set DEBUG=False with a real ALLOWED_HOSTS from the environment; trigger an error and confirm users get a generic 500, not a traceback.
  3. Add the HTTPS/cookie settings and re-run check --deploy; confirm those specific warnings clear.
  4. Move SECRET_KEY and the database config to environment variables; confirm the app runs with them set.
  5. Configure logging so a production error is written to a file; trigger one and find it in the log.
  6. Write your own pre-deploy checklist for Nidaan, ending with "check --deploy is clean".

Official documentation

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