Django Middleware: Request and Response Processing

Django middleware provides hooks into request and response processing enabling application-wide functionality that executes for every HTTP transaction. Middleware components form processing chains wrapping views with layers of functionality including authentication verification, session management, CSRF protection, security headers, logging, and performance monitoring. This powerful architecture enables cross-cutting concerns implementation without modifying individual views maintaining clean separation between business logic and infrastructure concerns. This comprehensive guide explores Django 6.0 middleware system including understanding middleware execution order and processing phases, using built-in middleware for security and optimization, creating custom middleware for logging and monitoring, implementing authentication and authorization middleware, adding custom headers and modifying responses, exception handling and error processing, performance profiling middleware, and best practices for middleware development. Mastering middleware unlocks powerful capabilities for implementing application-wide functionality efficiently transforming requests and responses consistently across entire applications from simple logging to complex security policies.
Understanding Middleware
Middleware are Python classes that process requests before they reach views and responses before they're sent to clients. Each middleware component can modify requests, generate responses short-circuiting the process, or augment responses. Middleware executes in the order defined in settings during request processing and in reverse order during response processing creating an onion-like architecture. Django includes essential middleware for security, sessions, authentication, CSRF protection, and common features.
# settings.py - Middleware configuration
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',
]
# Basic middleware structure
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization
def __call__(self, request):
# Code executed for each request before the view
response = self.get_response(request)
# Code executed for each request/response after the view
return response
# Middleware with process methods
class AdvancedMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Process request
response = self.get_response(request)
return response
def process_view(self, request, view_func, view_args, view_kwargs):
# Called just before Django calls the view
# Return None to continue, or HttpResponse to short-circuit
return None
def process_exception(self, request, exception):
# Called when view raises exception
# Return None or HttpResponse
return None
def process_template_response(self, request, response):
# Called for responses with render() method
return response
# Execution order
"""
Request Phase (top to bottom):
1. SecurityMiddleware
2. SessionMiddleware
3. CommonMiddleware
4. CsrfViewMiddleware
5. AuthenticationMiddleware
6. MessageMiddleware
7. View executes
Response Phase (bottom to top):
7. MessageMiddleware
6. AuthenticationMiddleware
5. CsrfViewMiddleware
4. CommonMiddleware
3. SessionMiddleware
2. SecurityMiddleware
1. Client receives response
"""Creating Custom Middleware
Custom middleware implements application-specific functionality affecting all requests and responses. Common use cases include request logging, performance monitoring, custom authentication, user tracking, response modification, and API throttling. Middleware provides centralized locations for cross-cutting concerns avoiding code duplication across views while maintaining separation of concerns.
# Logging middleware
import logging
import time
logger = logging.getLogger(__name__)
class RequestLoggingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Log request
start_time = time.time()
logger.info(f'{request.method} {request.path}')
response = self.get_response(request)
# Log response
duration = time.time() - start_time
logger.info(
f'{request.method} {request.path} '
f'Status: {response.status_code} '
f'Duration: {duration:.2f}s'
)
return response
# Performance monitoring middleware
class PerformanceMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
start_time = time.time()
response = self.get_response(request)
duration = time.time() - start_time
# Add performance header
response['X-Page-Generation-Duration'] = str(duration)
# Log slow requests
if duration > 1.0: # Slower than 1 second
logger.warning(
f'Slow request: {request.path} took {duration:.2f}s'
)
return response
# Custom header middleware
class CustomHeaderMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
# Add custom headers
response['X-Custom-Header'] = 'MyApp 1.0'
response['X-Request-ID'] = str(uuid.uuid4())
return response
# User tracking middleware
class UserTrackingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Track user activity
if request.user.is_authenticated:
from .models import UserActivity
UserActivity.objects.create(
user=request.user,
path=request.path,
method=request.method,
ip_address=self.get_client_ip(request)
)
response = self.get_response(request)
return response
def get_client_ip(self, request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
# API throttling middleware
from django.core.cache import cache
from django.http import HttpResponse
class ThrottleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.rate_limit = 100 # requests per minute
def __call__(self, request):
if request.path.startswith('/api/'):
ip = self.get_client_ip(request)
cache_key = f'throttle_{ip}'
requests = cache.get(cache_key, 0)
if requests >= self.rate_limit:
return HttpResponse(
'Rate limit exceeded',
status=429
)
cache.set(cache_key, requests + 1, 60) # 60 seconds
response = self.get_response(request)
return response
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]
return request.META.get('REMOTE_ADDR')
# Maintenance mode middleware
from django.shortcuts import render
class MaintenanceModeMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Check if maintenance mode is enabled
from django.conf import settings
if getattr(settings, 'MAINTENANCE_MODE', False):
# Allow staff users
if not request.user.is_staff:
return render(
request,
'maintenance.html',
status=503
)
response = self.get_response(request)
return responseException Handling Middleware
Middleware can catch and handle exceptions raised during request processing providing centralized error handling, logging, and custom error responses. The process_exception method receives exceptions before they bubble up enabling custom error pages, error notification, logging, and graceful degradation. This approach centralizes error handling logic avoiding repetition across views.
# Exception handling middleware
import logging
import traceback
from django.http import JsonResponse, HttpResponse
from django.shortcuts import render
logger = logging.getLogger(__name__)
class ExceptionHandlingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
try:
response = self.get_response(request)
return response
except Exception as e:
return self.handle_exception(request, e)
def process_exception(self, request, exception):
# Log exception
logger.error(
f'Exception in {request.path}: {str(exception)}\n'
f'{traceback.format_exc()}'
)
# Send error notification email
if not settings.DEBUG:
self.send_error_email(request, exception)
# Return custom error response
if request.path.startswith('/api/'):
return JsonResponse({
'error': 'Internal server error',
'message': str(exception) if settings.DEBUG else 'An error occurred'
}, status=500)
return render(request, '500.html', {
'exception': exception if settings.DEBUG else None
}, status=500)
def send_error_email(self, request, exception):
from django.core.mail import mail_admins
subject = f'Error on {request.path}'
message = f'''
URL: {request.build_absolute_uri()}
User: {request.user}
Exception: {str(exception)}
Traceback:
{traceback.format_exc()}
'''
mail_admins(subject, message, fail_silently=True)
# Database error handling
from django.db import OperationalError
class DatabaseErrorMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def process_exception(self, request, exception):
if isinstance(exception, OperationalError):
logger.critical('Database connection error')
return render(
request,
'errors/database_error.html',
status=503
)
return None
# Custom 404 middleware
from django.http import Http404
class Custom404Middleware:
def __init__(self, get_response):
self.get_response = get_response
def process_exception(self, request, exception):
if isinstance(exception, Http404):
# Log 404 errors
logger.warning(f'404 error: {request.path}')
# Suggest similar pages
suggestions = self.find_suggestions(request.path)
return render(request, '404.html', {
'suggestions': suggestions
}, status=404)
return None
def find_suggestions(self, path):
# Find similar URLs
from django.urls import get_resolver
resolver = get_resolver()
# Implementation to find similar paths
return []Authentication and Authorization Middleware
Custom authentication middleware enables alternative authentication schemes beyond Django's default session-based authentication. Common patterns include token authentication for APIs, JWT verification, API key validation, and IP-based access control. Middleware provides centralized authentication logic executing before views ensuring consistent security across applications.
# Token authentication middleware
from django.http import JsonResponse
from django.contrib.auth.models import AnonymousUser
class TokenAuthenticationMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Check for API token in header
token = request.META.get('HTTP_AUTHORIZATION')
if token and token.startswith('Bearer '):
token_key = token.split(' ')[1]
user = self.authenticate_token(token_key)
if user:
request.user = user
else:
return JsonResponse(
{'error': 'Invalid token'},
status=401
)
response = self.get_response(request)
return response
def authenticate_token(self, token_key):
from .models import APIToken
try:
api_token = APIToken.objects.select_related('user').get(
key=token_key,
is_active=True
)
return api_token.user
except APIToken.DoesNotExist:
return None
# IP whitelist middleware
class IPWhitelistMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.whitelist = ['127.0.0.1', '::1'] # localhost
def __call__(self, request):
ip = self.get_client_ip(request)
# Check admin access
if request.path.startswith('/admin/'):
if ip not in self.whitelist:
return HttpResponse('Access denied', status=403)
response = self.get_response(request)
return response
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]
return request.META.get('REMOTE_ADDR')
# JWT authentication middleware
import jwt
from django.conf import settings
class JWTAuthenticationMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
token = request.META.get('HTTP_AUTHORIZATION')
if token and token.startswith('Bearer '):
token_key = token.split(' ')[1]
try:
payload = jwt.decode(
token_key,
settings.SECRET_KEY,
algorithms=['HS256']
)
user_id = payload.get('user_id')
user = User.objects.get(id=user_id)
request.user = user
except (jwt.InvalidTokenError, User.DoesNotExist):
return JsonResponse(
{'error': 'Invalid token'},
status=401
)
response = self.get_response(request)
return responseMiddleware Best Practices
- Order matters: Place security middleware first, session before auth, and CSRF after session maintaining proper execution sequence
- Keep middleware focused: Each middleware should handle a single concern avoiding complex multi-purpose middleware
- Minimize processing: Avoid expensive operations in middleware as they execute for every request impacting performance
- Handle exceptions gracefully: Implement proper exception handling preventing middleware from breaking request processing
- Use early returns: Short-circuit processing when appropriate avoiding unnecessary view execution
- Document dependencies: Clearly document middleware dependencies on other middleware or settings
Conclusion
Django middleware provides powerful infrastructure for implementing application-wide functionality affecting all requests and responses through a clean, modular architecture. Understanding middleware execution order and processing phases enables proper middleware composition avoiding conflicts and ensuring security. Built-in middleware handles essential concerns including security headers, session management, authentication, and CSRF protection. Custom middleware implements application-specific requirements including logging, performance monitoring, custom authentication, throttling, and error handling. Exception handling middleware centralizes error processing providing consistent error responses and notifications. Authentication middleware enables alternative authentication schemes for APIs and specialized access control. Following best practices including proper ordering, focused functionality, minimal processing, graceful exception handling, and clear documentation ensures maintainable middleware implementations. Mastering middleware unlocks capabilities for cross-cutting concerns implementation transforming requests and responses consistently across applications from simple logging to sophisticated security policies throughout Django 6.0 development.
$ share --platform
$ cat /comments/ (0)
$ cat /comments/
// No comments found. Be the first!


