skip to content

Middleware Components

The MIDDLEWARE setting wraps every request and response in an ordered stack of factories with view, exception and template hooks. Interviewers probe built-in ordering and sync/async adaptation.

part ofDjangooverview, primer and where to startread it →
on this pageshow

explore

questions

11

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.
open as a page

How do you write a custom Django middleware, and which part of it runs once versus on every request?

level: juniorimportance: must knowfreq 62%

basics

~10 s

Write a factory that receives get_response: a class whose init(self, get_response) runs once at startup and whose call(self, request) runs per request, calling get_response and returning the response. Register its dotted path in MIDDLEWARE.

open as a page

How do you write a Django middleware, such as a tenant resolver, that runs in both WSGI and ASGI deployments without an adapter?

level: middleimportance: must knowfreq 45%

basics

~10 s

Declare a Django middleware hybrid with sync_and_async_middleware (or both class flags True), check inspect.iscoroutinefunction(get_response) once in the factory, and return an async def callable for async stacks and a plain function for sync ones.

open as a page

In Django, what do a middleware's sync_capable and async_capable attributes declare, and what are their defaults?

level: juniorimportance: should knowfreq 38%

basics

~20 s

They declare which request modes a Django middleware factory can handle: sync_capable defaults to True and async_capable to False, so an undecorated middleware is sync-only and Django adapts it with a thread hop under ASGI.

open as a page

In Django's MIDDLEWARE, where do GZipMiddleware, LocaleMiddleware and CommonMiddleware belong relative to the other built-ins, and why?

level: middleimportance: should knowfreq 40%

basics

~20 s

GZipMiddleware goes above anything that reads or changes the response body, so it compresses the final bytes. LocaleMiddleware goes after SessionMiddleware and before CommonMiddleware. CommonMiddleware stays near the top; middleware above it that changes the body must reset Content-Length.

open as a page

In Django, which middleware see the response when one returns early from __call__ versus from process_view?

level: middleimportance: should knowfreq 42%

basics

~20 s

An early return from call is seen only by that middleware and those listed above it; lower layers and the view never run. A response from process_view skips the view, yet every middleware's call after-phase still sees it.

open as a page

In Django middleware, when do process_view, process_exception and process_template_response run, and in what order across the MIDDLEWARE list?

level: middleimportance: should knowfreq 45%

basics

~20 s

process_view runs top-down after URL resolution, just before the view; process_exception runs bottom-up only when the view raises; process_template_response runs bottom-up when the response has render(). A process_view or process_exception response stops the remaining hooks of that kind.

open as a page

After a MIDDLEWARE reshuffle, a Django project's custom audit middleware fails with 'WSGIRequest' object has no attribute 'user'; how do you diagnose and fix it?

level: seniorimportance: should knowfreq 35%

basics

~20 s

request.user exists only after AuthenticationMiddleware has run on the way in. The audit middleware is listed above it, AuthenticationMiddleware was removed, or a layer above it returned early. Move the audit middleware below AuthenticationMiddleware, or read the user defensively.

open as a page

A Django timing middleware stores its start time on self in __call__; why are its durations wrong under concurrent load, and how do you fix it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Django creates one middleware instance per process at startup and every request shares it, so a concurrent request overwrites self.start and durations come out too short. Keep per-request state in a local variable in call or on the request object.

open as a page

After moving a Django project to ASGI with async views, every request still holds a thread and the log says 'Asynchronous handler adapted for middleware'; what is happening and how do you fix it?

level: seniorimportance: should knowfreq 33%

basics

~20 s

A sync-only middleware sits in the Django ASGI stack, so Django runs it and every sync-capable middleware outside it in a thread and reaches the async view through async_to_sync, holding that thread per request. Make it hybrid.

open as a page

How does Django's MiddlewareMixin run under ASGI, and why do its process_request and process_response hooks still hop into a thread?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

Django's MiddlewareMixin is sync- and async-capable; under ASGI call returns acall, which awaits get_response directly but runs each sync process_request and process_response hook through sync_to_async, so it hops per hook instead of holding a thread.

open as a page