Custom Middleware0%

Custom Middleware

Beginner12 min read•Updated: Jul 11, 2026
Study MaterialsCheatsheet

Custom Middleware

Django processes incoming HTTP requests and outgoing HTTP responses through an extensible pipeline known as Middleware. Each middleware component behaves like an onion layer: it can inspect, alter, or short-circuit incoming requests before they reach the view, and post-process outgoing responses before they are returned to the client browser.


1. The Middleware Execution Pipeline

Output
Incoming Request:
Client ──► [SecurityMiddleware] ──► [SessionMiddleware] ──► [AuthMiddleware] ──► View Function
│
Outgoing Response: ▼
Client ◄── [SecurityMiddleware] ◄── [SessionMiddleware] ◄── [AuthMiddleware] ◄── Response
  • Top-to-Bottom on Requests: Middlewares execute in the exact order listed in settings.MIDDLEWARE.
  • Bottom-to-Top on Responses: Middlewares execute in reverse order when returning responses.

2. Anatomy of Modern Callable Middleware

Modern Django middleware is structured as a Python callable (function or class) taking get_response:

Python
# config/middleware/request_timer.py
import time
import logging
 
logger = logging.getLogger("django.performance")
 
class PerformanceTimingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization upon server boot
 
def __call__(self, request):
# 1. Code executed BEFORE the view is called:
start_time = time.monotonic()
 
# 2. Hand over request to next layer (or view)
response = self.get_response(request)
 
# 3. Code executed AFTER the view completes:
duration = time.monotonic() - start_time
duration_ms = round(duration * 1000, 2)
 
# Attach custom performance header to outgoing HTTP response
response["X-Response-Time-Ms"] = str(duration_ms)
 
if duration_ms > 500:
logger.warning(
f"Slow Request Detected: {request.method} {request.path} took {duration_ms}ms"
)
 
return response

3. Special Middleware Hooks

Classes can implement specialized hook methods for specific interception points:

  • process_view(request, view_func, view_args, view_kwargs): Called just before Django calls the view function. Returning an HttpResponse bypasses the view entirely.
  • process_exception(request, exception): Called when a view raises an unhandled exception. Ideal for reporting crashes to Sentry or Datadog.
  • process_template_response(request, response): Called if the view returned a TemplateResponse object, allowing manipulation of template context before rendering.

4. IP Whitelisting & API Rate Limiting Middleware

Python
# config/middleware/ip_guard.py
from django.http import HttpResponseForbidden
from django.conf import settings
 
class IPWhitelistMiddleware:
def __init__(self, get_response):
self.get_response = get_response
 
def __call__(self, request):
# Restrict /admin/ access to corporate VPN IP ranges
if request.path.startswith("/admin/"):
client_ip = self.get_client_ip(request)
if client_ip not in settings.ADMIN_IP_WHITELIST:
return HttpResponseForbidden("Access Denied: IP address not in corporate whitelist.")
 
return self.get_response(request)
 
def get_client_ip(self, request):
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
if x_forwarded_for:
return x_forwarded_for.split(",")[0].strip()
return request.META.get("REMOTE_ADDR")

5. Registering Custom Middleware in settings.py

Python
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
# Custom Enterprise Middlewares:
"config.middleware.request_timer.PerformanceTimingMiddleware",
"config.middleware.ip_guard.IPWhitelistMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
]

Interactive Quiz

Practice Quiz

Question 1
In what order do middleware components execute during the lifecycle of an HTTP request and response?
Question 2
What does returning an HttpResponse directly from a middleware before calling get_response(request) do?
Question 3
How do you extract the real client IP address when Django sits behind a reverse proxy (e.g. Nginx or Cloudflare)?
Question 4
When is the process_exception(request, exception) hook called?
Question 5
Why is __init__(self, get_response) only executed once in class-based middleware?
Next Lesson

CSRF Protection

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Related Lessons

Previous LessonNext Lesson
Routing APIs with DRFCSRF Protection

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext