Which Django SESSION_COOKIE_* settings do you change before running on HTTPS in production, and what are their defaults?
answer
- one default favours runserver
- check --deploy warns
- two weeks, in seconds
- domain change strands old cookies
basics
~10 sDjango's SESSION_COOKIE_SECURE defaults to False and must be set to True on HTTPS; HTTPONLY (True), SAMESITE ('Lax'), AGE (1209600 seconds), NAME ('sessionid'), PATH ('/') and DOMAIN (None) are usually fine as they are.
solid answer
~40 s`SessionMiddleware` builds the cookie from the `SESSION_COOKIE_*` settings. Defaults: `SESSION_COOKIE_NAME = 'sessionid'`, `SESSION_COOKIE_AGE = 1209600` (two weeks), `SESSION_COOKIE_SECURE = False`, `SESSION_COOKIE_HTTPONLY = True`, `SESSION_COOKIE_SAMESITE = 'Lax'`, `SESSION_COOKIE_DOMAIN = None`, `SESSION_COOKIE_PATH = '/'`. The one to change is `SECURE`, which defaults to `False` so `runserver` works over HTTP; `manage.py check --deploy` flags it with `security.W010`–`W012`. Shorten `AGE` for sensitive sites, and change `DOMAIN` only to share across subdomains — changing it on a live site strands existing cookies on the old domain. `SESSION_EXPIRE_AT_BROWSER_CLOSE` and `SESSION_SAVE_EVERY_REQUEST` shape expiry. The cookie is only re-sent when the session is saved, so new flags reach a browser on its next save.
code
python · 6 lines# settings.py (production)
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True # default, stated for review
SESSION_COOKIE_SAMESITE = "Lax" # default, stated for review
SESSION_COOKIE_AGE = 60 * 60 * 8 # eight hours instead of two weeks
SESSION_EXPIRE_AT_BROWSER_CLOSE = Falsego deeper
Remember the default cookie name sessionid, that HttpOnly is on by default, and that Secure is off by default and must be turned on for HTTPS.
State every SESSION_COOKIE_* default, run check --deploy to catch the insecure ones, and explain that settings apply only when the session is next saved.
Plan changes to DOMAIN, NAME or AGE on a live site knowing they strand or log out existing cookies, and align the session cookie with the CSRF cookie settings.
Set a cookie policy across services sharing a parent domain: who owns SESSION_COOKIE_DOMAIN, how long sessions live, and how changes roll out without mass logouts.
## The settings and their defaults Django names every session-cookie attribute with a `SESSION_COOKIE_` setting. `SessionMiddleware` passes them to `response.set_cookie()` whenever it saves a session, and to `delete_cookie()` when it clears one. The defaults, from `django/conf/global_settings.py`: | Setting | Default | Production note | |---|---|---| | `SESSION_COOKIE_NAME` | `"sessionid"` | Change only to avoid collisions with other cookies | | `SESSION_COOKIE_AGE` | `1209600` (two weeks, in seconds) | Shorten for sensitive sites | | `SESSION_COOKIE_SECURE` | `False` | **Set to `True`** on an HTTPS site | | `SESSION_COOKIE_HTTPONLY` | `True` | Leave on | | `SESSION_COOKIE_SAMESITE` | `"Lax"` | `"Strict"`, `"None"` or `False` only with a reason | | `SESSION_COOKIE_DOMAIN` | `None` | Set only to share across subdomains | | `SESSION_COOKIE_PATH` | `"/"` | Narrow only when several Django sites share a host | Two related settings shape expiry: `SESSION_EXPIRE_AT_BROWSER_CLOSE` (default `False`) makes the cookie a browser-session cookie with no `Max-Age`, and `SESSION_SAVE_EVERY_REQUEST` (default `False`) re-sends the cookie on every request so its expiry slides forward. ## The one you must change `SESSION_COOKIE_SECURE` defaults to `False` so that `runserver` over plain HTTP works in development. On an HTTPS production site leave it `False` and the browser will send the session cookie over plain HTTP too. Django's deployment checklist catches it: ```bash python manage.py check --deploy ``` reports `security.W010`, `security.W011` or `security.W012` when sessions are in use and `SESSION_COOKIE_SECURE` is not `True`. The HttpOnly counterpart warnings are `security.W013` to `security.W015`, raised only if someone turned `SESSION_COOKIE_HTTPONLY` off. ## Settings with Django-specific side effects - **`SESSION_COOKIE_DOMAIN`.** Setting `".example.com"` shares the session across subdomains. The Django docs warn that changing it on a live site leaves existing cookies on the old domain, which can stop users logging in while those cookies persist. It also applies to the cookies `django.contrib.messages` sets. - **`SESSION_COOKIE_AGE`.** It is the cookie's `Max-Age` and the store's expiry for new sessions. For the `signed_cookies` engine it is also the server-side age limit for accepting a cookie. - **`SESSION_COOKIE_SAMESITE`.** `"Lax"` is the default; `False` omits the attribute entirely. An embedded or cross-site flow that needs `"None"` must also have `SESSION_COOKIE_SECURE = True`, because browsers reject `SameSite=None` without `Secure`. - **`SESSION_COOKIE_NAME`.** Renaming it logs everyone out once, since browsers keep sending the old name. - **`SESSION_COOKIE_PATH`.** Useful when two Django projects share one hostname under different URL prefixes, so each sees only its own cookie. ## When the cookie is actually sent The flags only matter when Django emits `Set-Cookie`, which happens when: 1. the session was modified (or `SESSION_SAVE_EVERY_REQUEST` is `True`), 2. it is not empty, and 3. the response status is below 500. A changed setting therefore reaches a given browser only the next time its session is saved. When a session becomes empty and the request carried a session cookie, the middleware deletes the cookie with the same name, path, domain and SameSite values. ## Where each setting is read - `SESSION_COOKIE_NAME` — read by `SessionMiddleware` to find the incoming key, and by the `file` engine as the prefix of every session file name. - `SESSION_COOKIE_AGE` — the cookie's `Max-Age` and the default expiry of stored sessions. - `SESSION_COOKIE_SECURE`, `SESSION_COOKIE_HTTPONLY` — passed to `set_cookie()` on save. - `SESSION_COOKIE_DOMAIN`, `SESSION_COOKIE_PATH`, `SESSION_COOKIE_SAMESITE` — passed to both `set_cookie()` and `delete_cookie()`, so a deletion targets the same cookie that was set. ## Practical production block ```python SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True # default, stated for review SESSION_COOKIE_SAMESITE = "Lax" # default, stated for review SESSION_COOKIE_AGE = 60 * 60 * 8 ``` Pair it with the CSRF cookie's equivalent (`CSRF_COOKIE_SECURE`) and HTTPS redirects, which are configured elsewhere in Django's security settings. What each browser attribute defends against is a protocol question; the Django question is which setting controls it and what the default is.
- What goes wrong if you change Django's SESSION_COOKIE_DOMAIN from None to '.example.com' on a live site?Browsers keep the cookies already set for the old host-only domain, so users can go on sending a stale session cookie next to the new one. The Django docs warn this may leave them unable to log in while those old cookies persist. The setting also affects the cookies `django.contrib.messages` sets.
- How do SESSION_EXPIRE_AT_BROWSER_CLOSE and SESSION_COOKIE_AGE interact in Django?With `SESSION_EXPIRE_AT_BROWSER_CLOSE = True` the middleware sends the cookie with no `Max-Age` or `Expires`, making it a browser-session cookie, while the store still expires the data by `SESSION_COOKIE_AGE`. With the default `False`, the cookie's `Max-Age` is the session's expiry age, `SESSION_COOKIE_AGE` unless `set_expiry()` changed it.
saying these in an interview costs you the question
- SESSION_COOKIE_SECURE defaults to True in Django
- SESSION_COOKIE_HTTPONLY must be switched on manually
- SESSION_COOKIE_AGE is measured in minutes
- Changing a cookie setting updates every browser's cookie immediately
- SESSION_COOKIE_SAMESITE defaults to 'Strict'