Middleware is Django's hook into request/response processing. Every HTTP request passes through a chain of middleware components before reaching your view, and every response passes back through the same chain in reverse. Middleware can inspect, modify, or short-circuit requests and responses globally, without touching individual views.
Django ships with essential middleware for security, sessions, authentication, CSRF protection, and more. Django 6 introduced ContentSecurityPolicyMiddleware for native CSP header management. You can also write custom middleware for cross-cutting concerns like logging, performance monitoring, rate limiting, and request enrichment. Understanding middleware ordering and the request/response lifecycle is critical to building secure and performant Django applications.
Master django middleware
Take the Django Security Best Practices course with hands-on lessons and challenges.
Django middleware uses a closure pattern. The outer function receives get_response (the next layer) and is called once at startup. The inner function runs on every request. Code before get_response() executes on the request path; code after executes on the response path.
A practical middleware that logs every request with its HTTP method, path, status code, and response time. Use time.monotonic() instead of time.time() for accurate duration measurement since it is not affected by system clock adjustments.
Class-based middleware supports optional hooks: process_view runs after URL resolution but before the view, and process_exception runs when a view raises an unhandled exception. This maintenance mode example short-circuits all requests and returns a 503 JSON response without hitting any view.
Django auto-detects whether middleware is sync or async based on whether the inner callable is a coroutine function. The dual-mode pattern works under both WSGI and ASGI. For ASGI-only deployments, you can define a simpler async-only middleware. Avoid blocking I/O (database queries, file reads) in async middleware without using sync_to_async.
Django 6 introduced built-in CSP support via ContentSecurityPolicyMiddleware, replacing the third-party django-csp package. Configure policies in SECURE_CSP using the CSP helper class. Use SECURE_CSP_REPORT_ONLY to test policies without enforcement first. The csp context processor provides {{ csp_nonce }} in templates for nonce-based inline script authorization.
Placing middleware in the wrong order — for example, putting AuthenticationMiddleware before SessionMiddleware
Django middleware has strict ordering requirements. SessionMiddleware must precede AuthenticationMiddleware (since auth reads from the session), and SecurityMiddleware should be first. Follow Django's default ordering and only insert custom middleware where it logically belongs (typically near the end).
Performing blocking I/O (database queries, HTTP calls, file reads) inside async middleware without wrapping it in sync_to_async
In ASGI deployments, synchronous I/O blocks the event loop and kills concurrency. Wrap any blocking operation with `from asgiref.sync import sync_to_async` or use an async-native library (e.g., httpx instead of requests).
Catching all exceptions in middleware and silently returning a 500 — this swallows errors and hides bugs
Let exceptions propagate to Django's error handling unless you have a specific reason to catch them. If you do catch exceptions in process_exception, always log the full traceback and re-raise or return an appropriate error response.
Adding expensive per-request logic (e.g., database queries, external API calls) in middleware that runs on every single request, including static files
Guard expensive middleware logic with path checks (e.g., skip for /static/ or /media/ paths) or move the logic to a decorator or mixin applied only to the views that need it. Middleware runs on every request — keep it lightweight.
Django middleware provides hooks into the request/response lifecycle for cross-cutting concerns like security, logging, and performance monitoring. Requests flow top-down through the MIDDLEWARE list and responses flow bottom-up. Each middleware is a callable wrapping get_response. Ordering matters — SecurityMiddleware first, SessionMiddleware before AuthenticationMiddleware. Django 6 added ContentSecurityPolicyMiddleware for native CSP support with nonce-based inline script authorization. For ASGI deployments, use async middleware to avoid blocking the event loop. Keep middleware lightweight since it runs on every request.
Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.
Interactive lessons and challenges, right in your code editor.
Check the free courses. No credit card.