skip to content

What order does Django's startproject MIDDLEWARE list use, and why must SessionMiddleware come before AuthenticationMiddleware?

level: juniorimportance: must knowfreq 66%

answer

  1. seven entries, security first
  2. who attaches request.session
  3. the user id lives in the session
  4. messages overflow into the session

basics

~10 s

Security, Session, Common, Csrf, Authentication, Messages, XFrameOptions. AuthenticationMiddleware reads the logged-in user's id from request.session, which SessionMiddleware attaches; listed first, it raises ImproperlyConfigured. The default message storage needs the session too.

solid answer

~40 s

`startproject` writes `SecurityMiddleware`, `SessionMiddleware`, `CommonMiddleware`, `CsrfViewMiddleware`, `AuthenticationMiddleware`, `MessageMiddleware`, `XFrameOptionsMiddleware`, in that order. The first entry sees the request first, so each middleware must come after the ones whose work it depends on. `SecurityMiddleware` is first so an HTTPS redirect happens before any other work. `SessionMiddleware` attaches `request.session`; `AuthenticationMiddleware` reads the user's id from that session and sets a lazy `request.user`, so it must come after it, and if it is listed first it raises `ImproperlyConfigured` telling you to move `SessionMiddleware` above it. `MessageMiddleware` must also follow `SessionMiddleware` because the default `FallbackStorage` includes session storage. `CsrfViewMiddleware` sits before authentication middleware that could log a user in. Django's admin checks (`admin.E408`-`E410`) only verify these are present, not their order.

code

python · 9 lines
python
# Broken: authentication runs before the session exists.
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
]
# Every request ends in a 500: ImproperlyConfigured, "The Django authentication
# middleware requires session middleware to be installed..."

go deeper

for a junior

Recall the seven default entries in order and the rule that SessionMiddleware comes before AuthenticationMiddleware and MessageMiddleware.

for a middle

Explain what each middleware attaches to the request, why a dependent middleware must follow its provider, and what error the swap produces.

for a senior

Point out that system checks test presence rather than order, and place custom middleware relative to the attributes and redirects it depends on.

for a principal

Treat the middleware list as shared infrastructure: agree placement rules for custom entries and consider a project check that enforces them.

## The default list A project generated by `django-admin startproject` gets this `MIDDLEWARE` setting (Django's own default for the setting is an empty list; the order below comes from the project template): ```python MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] ``` The first entry is the **outermost** layer: it sees the request first and the response last. So the rule of thumb is simple: a middleware that **uses** something another middleware **adds to the request** must be listed **after** it. ## Why each entry sits where it does | Position | Middleware | Why there | |---|---|---| | 1 | `SecurityMiddleware` | with `SECURE_SSL_REDIRECT` on, it redirects HTTP to HTTPS before the rest of the stack does any work | | 2 | `SessionMiddleware` | attaches `request.session`, which later entries read | | 3 | `CommonMiddleware` | near the top because it may redirect (`PREPEND_WWW` on the way in, `APPEND_SLASH` after a 404) and sets `Content-Length` on the way out | | 4 | `CsrfViewMiddleware` | before any view middleware that assumes CSRF has been checked, and before authentication middleware that may log a user in and rotate the token | | 5 | `AuthenticationMiddleware` | needs `request.session` to find the user | | 6 | `MessageMiddleware` | its default storage can keep messages in the session | | 7 | `XFrameOptionsMiddleware` | only sets a response header, so its position matters little | ## Session before authentication `AuthenticationMiddleware.process_request()` does two things: 1. checks that `request.session` exists, and if not raises `ImproperlyConfigured` with the message that the authentication middleware requires session middleware and that `SessionMiddleware` must be inserted before it; 2. sets `request.user` to a `SimpleLazyObject` that, on first access, reads the user's id and backend from the session and loads the user. If the two are swapped, `request.session` does not exist yet when step 1 runs, so every request fails. The exception is raised inside the middleware chain, so Django converts it to a 500 response; with `DEBUG` on, the error page shows the message naming the fix. ## Session before messages `MessageMiddleware` attaches a storage object to the request on the way in and saves unsent messages on the way out. The default `MESSAGE_STORAGE` is `FallbackStorage`, which tries a signed cookie first and falls back to the session for messages that do not fit. Building it creates a session storage, which raises `ImproperlyConfigured` when `request.session` is missing. With `CookieStorage` configured instead, messages would not need the session, but the default does. ## What Django checks, and what it does not - When `django.contrib.admin` is installed, the admin's system checks report `admin.E408`, `admin.E409` and `admin.E410` if `AuthenticationMiddleware`, `MessageMiddleware` or `SessionMiddleware` is **missing** from `MIDDLEWARE`. - These checks look for **presence**, not order. `admin.E410`'s hint mentions putting `SessionMiddleware` before `AuthenticationMiddleware`, but a list with both present in the wrong order passes the checks and fails only at request time. - Nothing reorders the list for you: Django builds the chain exactly as written. ## Adding your own middleware - Anything that reads `request.user` goes **below** `AuthenticationMiddleware`. - Anything that reads or writes `request.session` goes **below** `SessionMiddleware`. - Anything that must see every request, including HTTPS redirects, goes **above** `SecurityMiddleware`. - Anything that rewrites the response body goes **above** `CommonMiddleware`, or resets `Content-Length` itself.

  • Does manage.py check catch AuthenticationMiddleware listed above SessionMiddleware?
    No. With the admin installed, `admin.E408`, `admin.E409` and `admin.E410` fire only when `AuthenticationMiddleware`, `MessageMiddleware` or `SessionMiddleware` is missing. A list containing all three in the wrong order passes the checks, and the problem appears at request time as `ImproperlyConfigured` from `AuthenticationMiddleware`. A project-level check can assert the order if you want it enforced.
  • Why is SecurityMiddleware listed first?
    With `SECURE_SSL_REDIRECT` enabled it answers plain-HTTP requests with a permanent redirect to HTTPS. Placed first, that redirect happens before sessions are loaded or any other middleware runs, so insecure requests cost almost nothing and never touch session cookies. On the way out it adds whichever security headers its settings enable.

saying these in an interview costs you the question

  • Middleware order does not matter as long as every entry is listed.
  • Django's system checks reject a MIDDLEWARE list in the wrong order.
  • With AuthenticationMiddleware first, users are silently treated as anonymous.
  • Django sorts MIDDLEWARE at startup to satisfy dependencies.
  • MessageMiddleware never needs sessions because messages are stored in cookies.