What can Django's signed_cookies session engine not do that the database engine can, and when is it still a reasonable choice?
answer
- no record on the server
- logout clears one browser
- signed is not encrypted
- size rides every request
basics
~20 sDjango's signed_cookies engine keeps the whole session in a signed, unencrypted cookie, so it cannot revoke a session server-side, cannot hide data from the user and is capped by cookie size; it suits small, non-sensitive state.
solid answer
~40 sWith `signed_cookies` the serialized session is compressed and signed with `SECRET_KEY` and becomes the cookie value; there is no server-side record. So logout's `flush()` only clears the cookie in that browser — a copied cookie stays valid until it is older than `SESSION_COOKIE_AGE`, and replay is possible because a signature proves origin, not freshness. The data is readable by the user, the browser's per-cookie size limit caps it, and it travels on every request. Your levers are coarse: a password change breaks the auth session hash, rotating `SECRET_KEY` kills every cookie, and a short `SESSION_COOKIE_AGE` bounds the damage. `clearsessions` has nothing to do. It is reasonable for tiny, non-sensitive, tamper-evident state on a stateless fleet, not for logins that must be killable.
code
python · 5 lines# settings.py - acceptable only for small, non-sensitive session data
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
SESSION_COOKIE_HTTPONLY = True # default; keeps page scripts from reading it
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_AGE = 60 * 60 * 4 # bounds how long a copied cookie keeps workinggo deeper
Know that signed_cookies keeps the session in the browser, signed but readable, and that logout cannot kill copies of it.
Explain how the engine signs and compresses the serialized dict, why flush() only affects one browser, and how SESSION_COOKIE_AGE bounds acceptance.
Argue when revocation matters, list the coarse levers (password change, SECRET_KEY rotation, short age), and keep sensitive or growing data out of the cookie.
Trade stateless scaling against losing server-side control of sessions; for authenticated products the default should be a server-side engine.
## How the signed_cookies engine works With `SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"`, Django keeps **no server-side session record**. On save, the engine serializes the whole session dict (with `SESSION_SERIALIZER`, JSON by default), compresses it and signs it with `django.core.signing` using `SECRET_KEY` and a fixed salt. That signed string **is** the session key, and it goes into the cookie named by `SESSION_COOKIE_NAME`. On load, the engine verifies the signature and an age limit of `SESSION_COOKIE_AGE`; any failure resets the session to empty. It is attractive for stateless fleets: no database table, no cache, every app server can read every session. ## What it cannot do | Capability | `db` / `cached_db` | `signed_cookies` | |---|---|---| | Revoke one session server-side | Yes, delete the record | **No**, there is no record | | Invalidate on logout everywhere | Yes, `flush()` deletes the row | **Only this browser's cookie is cleared** | | Keep data secret from the user | Yes, only the key is sent | **No**, signed but not encrypted | | Hold a large session | Limited by the store | Limited by the browser's per-cookie size (commonly 4096 bytes) | | Clean up expired sessions | `clearsessions` deletes rows | Nothing to clean; `clear_expired()` is a no-op | ## Why logout does not end a stolen session On logout Django calls `request.session.flush()`. For a server-side engine that deletes the stored record, so every copy of the old cookie now points at nothing. For `signed_cookies`, `delete()` just empties the session in memory, and the middleware then deletes the cookie in **that browser** (or replaces it if the view stores new data). A copy of the old cookie captured earlier is still correctly signed and still younger than `SESSION_COOKIE_AGE`, so it still loads. The Django docs state this plainly: cookie-based sessions are not invalidated when a user logs out, and a stolen cookie keeps working until it is older than `SESSION_COOKIE_AGE`. The same property allows **replay**: the signature proves the data came from your site, not that it is the latest version you issued. ## The levers you do have - **Password change.** `django.contrib.auth` stores a session auth hash derived from the user's password hash in the session. When the user's password changes, `get_user()` finds a mismatch and flushes the session, so stolen cookies stop authenticating that user. Non-auth data in the stolen cookie remains readable. - **Rotating `SECRET_KEY`.** Invalidates every signed cookie at once, for all users, unless the old key stays in `SECRET_KEY_FALLBACKS`. A blunt instrument. - **A short `SESSION_COOKIE_AGE`.** Bounds how long a stolen cookie works, at the cost of more frequent logins. - **Keep secrets out.** Anything in the session is readable by the user, so no prices a user must not tamper with (tampering is detected, reading is not prevented), no internal flags, no personal data. ## Other operational traps 1. **Size.** Django compresses the payload, but a growing basket or a list of recent items can exceed the browser's cookie limit; the browser may then drop the cookie, and the session silently resets. 2. **Bandwidth.** The whole session travels on every request to the cookie's path. 3. **`SESSION_SAVE_EVERY_REQUEST`** means re-signing and re-sending the cookie on every response. 4. **Custom expiry.** The engine checks age against `SESSION_COOKIE_AGE`; the source notes that a non-default expiry set with `set_expiry()` is not reflected in that server-side age check. 5. **`SESSION_COOKIE_HTTPONLY`** should stay `True` (its default) so page scripts cannot read the data. ## Checklist before choosing it - Is every value in the session small, bounded and safe for the user to read? - Is it acceptable that logout cannot kill copies of the cookie? - Is `SESSION_COOKIE_AGE` short enough to bound a stolen cookie's life? - Do login sessions share this cookie? If so, revocation requirements usually decide against it. ## When it is the right choice Small, non-sensitive, tamper-evident state where server-side revocation is not a requirement: a UI preference, a short wizard step, an anonymous visitor's locale. For authenticated sessions where "log out everywhere" or "kill this session" must work, a server-side engine such as `cached_db` is the defensible default.
- With Django's signed_cookies engine, does changing a user's password invalidate a stolen session cookie?For authentication, yes. `django.contrib.auth` stores a session auth hash derived from the password hash; after a password change `get_user()` sees a mismatch and flushes the session, so the stolen cookie no longer logs the attacker in. Any non-auth data in that cookie is still readable by whoever holds it.
- What happens to every Django session when SECRET_KEY is rotated without SECRET_KEY_FALLBACKS?Signatures made with the old key stop verifying. For `signed_cookies` every cookie fails and the session resets to empty; database-backed sessions are signed too, so their stored data also becomes unreadable. Listing the old key in `SECRET_KEY_FALLBACKS` lets existing sessions keep working while new ones are signed with the new key.
saying these in an interview costs you the question
- Signed cookie sessions are encrypted, so users cannot read them
- Logging out invalidates every copy of a signed session cookie
- clearsessions must run to expire signed cookie sessions
- A valid signature proves the cookie is the latest one issued
- Signed cookies can hold arbitrarily large sessions because Django compresses them