Django Middleware
Every request that comes into your Django application and every response that goes back out passes through a series of layers called Middleware. Middleware is code that runs automatically for every single request and response — before the view processes it and after the view sends the response back. It is the most powerful way to apply global logic across your entire application.
What is Middleware?
Middleware is a layer of code that sits between the web server and your Django views. It can inspect, modify, block, or enhance requests coming in and responses going out — all without touching any individual view.
Example: Think of a large hotel. Every guest who enters (request) must pass through a check-in counter (middleware). The counter checks their ID (authentication middleware), records their arrival time (logging middleware), and gives them a room card (session middleware). When the guest leaves (response), they pass through checkout (middleware), return the key, and get their bill. Neither the hotel room (view) nor the guests (users) need to know about these checks — they happen automatically for every guest.
Django's Default Middleware Stack
Open settings.py and you will find the MIDDLEWARE list already set up with Django's built-in middleware:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', # Security headers
'django.contrib.sessions.middleware.SessionMiddleware', # Session handling
'django.middleware.common.CommonMiddleware', # URL redirects (www, slashes)
'django.middleware.csrf.CsrfViewMiddleware', # CSRF protection
'django.contrib.auth.middleware.AuthenticationMiddleware', # User authentication
'django.contrib.messages.middleware.MessageMiddleware', # Flash messages
'django.middleware.clickjacking.XFrameOptionsMiddleware', # Clickjacking protection
]
The order matters! Middleware runs from top to bottom on the way in (request), and from bottom to top on the way out (response).
What Each Default Middleware Does
- SecurityMiddleware — Adds security headers to responses, handles HTTPS redirects.
- SessionMiddleware — Enables the session system so you can use
request.session. - CommonMiddleware — Redirects URLs without trailing slashes, and handles
wwwredirects. - CsrfViewMiddleware — Checks CSRF tokens on all POST requests — protects against fake form submissions.
- AuthenticationMiddleware — Attaches the logged-in user to every request as
request.user. - MessageMiddleware — Enables the messages framework for flash notifications.
- XFrameOptionsMiddleware — Prevents your pages from being loaded inside an iframe on other websites (clickjacking protection).
How Middleware Works — The Flow
Browser → Request
↓
SecurityMiddleware (request)
↓
SessionMiddleware (request)
↓
AuthenticationMiddleware (request) — attaches request.user
↓
Your View runs and returns a Response
↓
MessageMiddleware (response)
↓
SessionMiddleware (response) — saves session
↓
SecurityMiddleware (response) — adds security headers
↓
Browser ← Response
Creating Custom Middleware
You can write your own middleware to add any global logic your application needs. A middleware is a Python class with specific method names.
Basic Middleware Structure
# school/middleware.py
class MyCustomMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time setup code goes here (runs when the server starts)
def __call__(self, request):
# Code here runs BEFORE the view processes the request
response = self.get_response(request) # Call the view (and other middleware)
# Code here runs AFTER the view has processed the request
return response
Registering Your Middleware in settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
...
'school.middleware.MyCustomMiddleware', # <-- Add your middleware here
]
Practical Example 1 — Request Logging Middleware
Log every page visit with the URL and time taken to process it:
# school/middleware.py
import time
class RequestLogMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
start_time = time.time()
# Before the view — record start time
print(f'--> Request: {request.method} {request.path}')
response = self.get_response(request)
# After the view — calculate time taken
duration = time.time() - start_time
print(f'<-- Response: {response.status_code} in {duration:.3f} seconds')
return response
Example: This middleware is like a security checkpoint at a factory gate. Every vehicle entering (request) is logged with the time and destination. When it leaves (response), the time and exit status are recorded. The factory workers (views) never need to do this logging themselves — the checkpoint handles it for all vehicles automatically.
Practical Example 2 — Maintenance Mode Middleware
Block all users and show a maintenance message when your site is under maintenance:
# school/middleware.py
from django.http import HttpResponse
class MaintenanceModeMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Allow admin access even during maintenance
if request.path.startswith('/admin/'):
return self.get_response(request)
# Block all other pages
MAINTENANCE_MODE = True # Change to False when maintenance is done
if MAINTENANCE_MODE:
return HttpResponse(
'<h1>Site Under Maintenance</h1><p>We will be back soon!</p>',
status=503
)
return self.get_response(request)
Practical Example 3 — Restrict Access by IP Address
# school/middleware.py
from django.http import HttpResponseForbidden
BLOCKED_IPS = ['192.168.1.100', '10.0.0.5']
class BlockIPMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
user_ip = request.META.get('REMOTE_ADDR')
if user_ip in BLOCKED_IPS:
return HttpResponseForbidden('Your IP address has been blocked.')
return self.get_response(request)
Middleware Order Matters
The order of middleware in the MIDDLEWARE list is critical. For example:
SessionMiddlewaremust come beforeAuthenticationMiddlewarebecause authentication depends on sessions being available.- Your custom middleware that needs
request.usermust come afterAuthenticationMiddleware. - Any middleware that sends a response early (like the maintenance mode middleware) should generally be placed near the top of the list.
Example: Middleware order is like a production line in a factory. Station 1 cuts the metal. Station 2 bends it. Station 3 paints it. You cannot paint before bending or cut after painting — the order is critical for the final product to come out correctly.
Quick Recap
- Middleware runs automatically for every request and response — no need to call it in views.
- Django includes several built-in middlewares for security, sessions, CSRF, and authentication.
- Create custom middleware as a Python class with
__init__and__call__methods. - Register custom middleware in the
MIDDLEWARElist insettings.py. - Code before
self.get_response(request)runs on the way in (request phase). - Code after
self.get_response(request)runs on the way out (response phase). - Middleware order in
settings.pymatters — place them carefully based on dependencies.
