Middleware
Middleware in Django is a layer of code that wraps every request and response — each middleware sees the request on the way in and the response on the way out, letting you add cross-cutting behaviour like authentication, logging, or security headers in one place instead of in every view.
Learn Middleware in our free Django course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Django course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll learn how the request/response cycle flows through the middleware stack, how to write your own middleware class, and which hooks and built-in middleware Django gives you out of the box.
When a request arrives, Django does not hand it straight to your view. It first passes the request through a stack of middleware — small components listed in the MIDDLEWARE setting. The request travels down the stack to reach the view, and the response travels back up the stack to reach the browser.
This is the onion model : the first middleware in the list is the outermost layer. It sees the request first and the response last. The last middleware is the innermost layer, closest to the view.
A default settings.py already lists several middleware. The order is deliberate — for example, security checks should wrap everything, and session handling must run before authentication because authentication reads the session.
The modern way to write middleware is a class with two methods. __init__(self, get_response) runs once when the server boots and stores the get_response callable — the next layer of the onion. __call__(self, request) runs for every request .
Inside __call__ you write code before calling get_response(request) (the request phase) and code after it returns (the response phase). You must always return the response at the end.
To activate it, add its dotted path to the MIDDLEWARE list:
Beyond __call__ , middleware can define optional hook methods . Django calls them automatically at specific points in the cycle if they exist:
Django ships with many built-in middleware so common needs are handled for you:
Fill in the blank so the middleware correctly calls the next layer. The __call__ method must call get_response(request) and return its result.
❌ Forgetting to return the response from __call__
If __call__ calls get_response(request) but does not return it, Django gets None and raises a "didn't return an HttpResponse object" error.
✅ Fix: always end __call__ with return response .
If your middleware never calls get_response(request) , the inner layers and the view never run, so no real response is produced.
✅ Fix: call response = self.get_response(request) unless you intentionally short-circuit with your own response.
Placing AuthenticationMiddleware before SessionMiddleware breaks request.user , because auth reads the session that hasn't been attached yet.
✅ Fix: keep SessionMiddleware above AuthenticationMiddleware and follow Django's documented default order.
Write middleware that short-circuits the onion: if the request path is blocked, return a 403 response without calling the view. Otherwise call get_response as normal.
Lesson 22 complete — you understand the middleware stack!
You now know how the request/response cycle flows through the onion of middleware, how to write a custom middleware class with __init__ and __call__ , how to use hooks like process_view and process_exception , and which built-in middleware Django provides.
🚀 Up next: Testing Django — learn how to write tests that verify your views, models, and middleware behave correctly.
Practice quiz
Where does middleware sit in the request flow?
- Between the web server and your views
- Inside the database
- In the browser only
- After the response is sent
Answer: Between the web server and your views. Middleware is a layer between the server and your views.
In the onion model, what does the FIRST middleware in MIDDLEWARE do?
- Runs only on errors
- Sees the request first and the response last
- Never runs
- Sees the response first
Answer: Sees the request first and the response last. The first middleware is the outermost layer of the onion.
Which method is called once at startup and stores get_response?
- __call__
- process_view
- __init__(self, get_response)
- handle
Answer: __init__(self, get_response). __init__(self, get_response) runs once and stores the next layer.
Which method runs for every request?
- __init__
- __call__(self, request)
- process_exception
- ready
Answer: __call__(self, request). __call__(self, request) is invoked per request.
What must __call__ always return?
- None
- The request
- The response
- A status code
Answer: The response. It must return the response or Django raises an error.
Which hook runs just before Django calls the view?
- process_exception
- process_template_response
- process_view
- process_request
Answer: process_view. process_view runs just before the view is called.
When does process_exception run?
- On every request
- At server startup
- After every response
- Only when the view raises an unhandled exception
Answer: Only when the view raises an unhandled exception. process_exception runs only on an unhandled view exception.
Why must SessionMiddleware come before AuthenticationMiddleware?
- Alphabetical order
- Auth reads the session, which must be attached first
- Sessions need auth
- It does not matter
Answer: Auth reads the session, which must be attached first. Authentication reads request.session, so the session must be set first.
Where do you activate a custom middleware?
- By adding its dotted path to the MIDDLEWARE list
- In urls.py
- In the model
- It is automatic
Answer: By adding its dotted path to the MIDDLEWARE list. Add the class's dotted path to MIDDLEWARE in settings.py.
What does the response phase order look like across the stack?
- Top to bottom
- Random
- Same as request phase
- Bottom to top (reverse of the request phase)
Answer: Bottom to top (reverse of the request phase). Responses travel outward, bottom-to-top, the reverse of requests.