Why is Django's default MESSAGE_STORAGE FallbackStorage, and what goes wrong if you use CookieStorage or SessionStorage alone?
answer
- cookie first, then overflow
- a two-kilobyte ceiling
- every notice writes the session
- DEBUG turns loss into an error
basics
~20 sDjango's FallbackStorage keeps short notices in a signed messages cookie and spills overflow into the session, so the common case needs no session write. CookieStorage alone drops the oldest messages past 2048 bytes; SessionStorage alone saves the session for every notice.
solid answer
~40 s`MESSAGE_STORAGE` defaults to `FallbackStorage`, which tries `CookieStorage` first and passes whatever does not fit to `SessionStorage`. `CookieStorage` writes a signed, readable cookie named `messages`, capped at `max_cookie_size = 2048` bytes, and reuses the `SESSION_COOKIE_*` flags; alone, it drops the oldest messages that do not fit — `MessageMiddleware` raises `ValueError("Not all temporary messages could be stored.")` under `DEBUG`, and in production they are silently lost. `SessionStorage` stores the list under `request.session["_messages"]`, needs the session middleware before `MessageMiddleware` or raises `ImproperlyConfigured`, and modifies the session on every notice — creating a session for anonymous visitors. Fallback gives the cheap cookie path for one short notice and still delivers bursts. Choose `SessionStorage` when message text must not be client-readable.
code
python · 6 lines# settings.py
# Default; shown for review:
MESSAGE_STORAGE = "django.contrib.messages.storage.fallback.FallbackStorage"
# Keep notice text out of the browser (every user already has a session):
# MESSAGE_STORAGE = "django.contrib.messages.storage.session.SessionStorage"go deeper
Know that messages are stored between requests in a cookie, the session, or both, and that the default tries the cookie first.
Describe the three storage classes, the 2048-byte cookie cap and the _messages session key, and what each needs installed.
Reason about silent loss with CookieStorage in production, session writes and anonymous sessions with SessionStorage, and readable text in cookies.
Treat notice storage as part of the session footprint: decide whether anonymous traffic may create sessions and what client-visible data is acceptable.
## Where Django keeps a message between requests A message added with `messages.success(request, "Profile saved.")` must survive the redirect to the next request. The `MESSAGE_STORAGE` setting picks the class that stores it; the default is `django.contrib.messages.storage.fallback.FallbackStorage`. Django ships three: | Storage | Where messages live | Size limit | Needs sessions | |---|---|---|---| | `cookie.CookieStorage` | A signed cookie named `messages` | 2048 bytes of cookie value (`max_cookie_size`) | No | | `session.SessionStorage` | The session, under the key `_messages` | Whatever the session engine allows | **Yes** | | `fallback.FallbackStorage` (**default**) | Cookie first, overflow in the session | None in practice | **Yes** installed; written only on overflow | ## CookieStorage alone - The cookie is **signed** with Django's cookie signer (derived from `SECRET_KEY`, with a fixed salt), so tampering is detected, but the text is **readable** by the client. - It reuses the session cookie flags: `SESSION_COOKIE_DOMAIN`, `SESSION_COOKIE_SECURE`, `SESSION_COOKIE_HTTPONLY` and `SESSION_COOKIE_SAMESITE`. - When the encoded messages exceed `max_cookie_size`, it keeps the newest messages that fit and **drops the oldest**, adding a marker that says not everything fit. The dropped messages are returned to the middleware as "unstored": with `DEBUG = True` `MessageMiddleware` raises `ValueError("Not all temporary messages could be stored.")`; in production they are silently lost. - It needs no session, so anonymous visitors cost nothing server-side. ## SessionStorage alone - It stores the serialized list in `request.session["_messages"]`, so it can hold many or long messages. - It requires `request.session`; if the session middleware is missing or runs after `MessageMiddleware`, creating the storage raises `ImproperlyConfigured`. - Every notice **modifies the session**, which means a session save and a session cookie. For an anonymous visitor that means creating a session (a database row with the default `db` engine) just to say "Thanks for subscribing." - It inherits the session engine's properties: with the `signed_cookies` engine the messages end up in a cookie anyway. ## Why FallbackStorage is the default `FallbackStorage` tries `CookieStorage` first and passes anything that did not fit to `SessionStorage`. It builds both storages for every request, so it needs the session middleware installed even though it writes to the session only on overflow: 1. On save, the cookie storage keeps the **oldest** messages that fit (it is called with `remove_oldest=False`), and the newer overflow goes to the session. 2. On read, it reads the cookie; only if the cookie says not everything fit does it also read the session. 3. A storage that held messages last time is cleared when they are consumed, even if nothing new goes into it. The common case — one short notice after a redirect — therefore costs a small cookie and **no session write**, and the rare burst of long messages still gets delivered instead of dropped. ## Choosing - Keep the default unless you have a reason. - Pick `SessionStorage` if message text must not be readable client-side, or if messages are long and frequent, and every user already has a session anyway. - Pick `CookieStorage` only when sessions are not installed, and keep messages short and few. - Whatever you pick, messages are consumed the same way: when a template iterates `messages`. ## Where to look when a notice goes missing - **CookieStorage**: inspect the `messages` cookie on the redirect response; if it carries the not-finished marker, older notices were trimmed, and in production nothing reports it. - **SessionStorage**: check `request.session["_messages"]` between requests, and confirm the session was actually saved (a 5xx response skips the session save). - **FallbackStorage**: check both, in that order; the session holds only what overflowed from the cookie. - **Any storage**: confirm the landing page's template iterates `messages`, since storage only decides where a notice waits, not when it is shown. ## Operational notes - Message text is marked safe again on load only if it was a `SafeData` string when stored; plain strings are escaped as usual in templates. - The behaviour for parallel requests from the same client is undefined for cookie- and session-backed storage, per the Django docs. - `MessageMiddleware` must come after `SessionMiddleware` for any storage that touches the session, including the default.
- When Django's FallbackStorage overflows, which messages stay in the cookie and which go to the session?The fallback calls the cookie storage with `remove_oldest=False`, so the cookie keeps the oldest messages that fit and adds a marker saying more exist; the newer overflow goes to `SessionStorage`. On read, the session is consulted only when the cookie carries that marker, so a normal request never touches the session.
- Can a user read or change the text of a Django message stored by CookieStorage?Read, yes: the cookie is signed, not encrypted. Change, no: it is signed with Django's cookie signer and a fixed salt, and on a bad signature the storage discards the data and marks it used so the cookie is removed. Keep sensitive details out of message text, or use `SessionStorage`.
saying these in an interview costs you the question
- The default message storage is SessionStorage
- CookieStorage encrypts the message text
- Messages that do not fit in the cookie always raise an error
- FallbackStorage writes every message to both the cookie and the session
- SessionStorage works without SessionMiddleware