Middleware Development
Learn to create custom middleware for request/response processing. This is a foundational concept in Python web development that professional developers rely on daily. The explanations below are written to be beginner-friendly while covering the depth and nuance that comes from real-world Python/Django experience. Take your time with each section and practice the examples
75 min•By Priygop Team•Last updated: Feb 2026
Middleware Fundamentals
Middleware is a framework of hooks into Django's request/response processing. It's a way to process requests globally before they reach the view. Custom middleware can handle authentication, logging, rate limiting, and more.
Custom Middleware Implementation
Example
# Custom Middleware Development
# 1. Basic Middleware Structure
class CustomMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Code executed before the view
response = self.get_response(request)
# Code executed after the view
return response
def process_view(self, request, view_func, view_args, view_kwargs):
# Code executed before the view is called
return None # Continue processing
def process_exception(self, request, exception):
# Code executed when an exception occurs
return None # Let Django handle the exception
def process_template_response(self, request, response):
# Code executed after the view returns a response
return response
# 2. Authentication Middleware
from django.contrib.auth.models import AnonymousUser
from django.core.cache import cache
import jwt
class JWTAuthMiddleware:
"""JWT Authentication Middleware"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Extract JWT token from headers
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
if auth_header.startswith('Bearer '):
token = auth_header[7:] # Remove 'Bearer ' prefix
try:
# Decode JWT token
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
user_id = payload.get('user_id')
if user_id:
# Get user from cache or database
user = cache.get(f'user_{user_id}')
if not user:
from django.contrib.auth.models import User
user = User.objects.get(id=user_id)
cache.set(f'user_{user_id}', user, 300) # Cache for 5 minutes
request.user = user
else:
request.user = AnonymousUser()
except (jwt.InvalidTokenError, User.DoesNotExist):
request.user = AnonymousUser()
else:
request.user = AnonymousUser()
response = self.get_response(request)
return response
# 3. Rate Limiting Middleware
from django.core.cache import cache
from django.http import HttpResponseTooManyRequests
import time
class RateLimitMiddleware:
"""Rate limiting middleware"""
def __init__(self, get_response):
self.get_response = get_response
self.rate_limits = {
'default': {'requests': 100, 'window': 3600}, # 100 requests per hour
'api': {'requests': 1000, 'window': 3600}, # 1000 requests per hour
'login': {'requests': 5, 'window': 300}, # 5 requests per 5 minutes
}
def __call__(self, request):
# Get client identifier
client_id = self._get_client_id(request)
# Check rate limit
if not self._check_rate_limit(request, client_id):
return HttpResponseTooManyRequests(
'Rate limit exceeded. Please try again later.'
)
response = self.get_response(request)
return response
def _get_client_id(self, request):
"""Get unique client identifier"""
if request.user.is_authenticated:
return f"user_{request.user.id}"
else:
return f"ip_{request.META.get('REMOTE_ADDR', 'unknown')}"
def _check_rate_limit(self, request, client_id):
"""Check if client has exceeded rate limit"""
# Determine rate limit type
if request.path.startswith('/api/'):
limit_type = 'api'
elif request.path.startswith('/login/'):
limit_type = 'login'
else:
limit_type = 'default'
limit_config = self.rate_limits[limit_type]
cache_key = f"rate_limit_{limit_type}_{client_id}"
# Get current request count
current_count = cache.get(cache_key, 0)
if current_count >= limit_config['requests']:
return False
# Increment count
cache.set(cache_key, current_count + 1, limit_config['window'])
return True
# 4. Logging and Monitoring Middleware
import logging
import time
from django.utils.deprecation import MiddlewareMixin
class LoggingMiddleware(MiddlewareMixin):
"""Logging and monitoring middleware"""
def __init__(self, get_response):
self.get_response = get_response
self.logger = logging.getLogger('django.request')
def __call__(self, request):
# Start timing
start_time = time.time()
# Log request
self.logger.info(f"Request started: {request.method} {request.path}")
# Process request
response = self.get_response(request)
# Calculate duration
duration = time.time() - start_time
# Log response
self.logger.info(
f"Request completed: {request.method} {request.path} "
f"- Status: {response.status_code} - Duration: {duration:.3f}s"
)
# Add custom headers
response['X-Request-Duration'] = f"{duration:.3f}s"
response['X-Request-ID'] = self._generate_request_id()
return response
def _generate_request_id(self):
"""Generate unique request ID"""
import uuid
return str(uuid.uuid4())
def process_exception(self, request, exception):
"""Log exceptions"""
self.logger.error(
f"Request failed: {request.method} {request.path} - Error: {str(exception)}"
)
return None
# 5. CORS Middleware
from django.http import JsonResponse
class CORSMiddleware:
"""Custom CORS middleware"""
def __init__(self, get_response):
self.get_response = get_response
self.allowed_origins = [
'http://localhost:3000',
'https://yourdomain.com',
'https://api.yourdomain.com'
]
self.allowed_methods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
self.allowed_headers = [
'Content-Type',
'Authorization',
'X-Requested-With'
]
def __call__(self, request):
# Handle preflight request
if request.method == 'OPTIONS':
return self._handle_preflight(request)
response = self.get_response(request)
# Add CORS headers
origin = request.META.get('HTTP_ORIGIN')
if origin in self.allowed_origins:
response['Access-Control-Allow-Origin'] = origin
response['Access-Control-Allow-Credentials'] = 'true'
return response
def _handle_preflight(self, request):
"""Handle preflight OPTIONS request"""
origin = request.META.get('HTTP_ORIGIN')
if origin not in self.allowed_origins:
return JsonResponse({'error': 'Origin not allowed'}, status=403)
response = JsonResponse({})
response['Access-Control-Allow-Origin'] = origin
response['Access-Control-Allow-Methods'] = ', '.join(self.allowed_methods)
response['Access-Control-Allow-Headers'] = ', '.join(self.allowed_headers)
response['Access-Control-Allow-Credentials'] = 'true'
response['Access-Control-Max-Age'] = '86400' # 24 hours
return response
# 6. security Middleware
from django.http import HttpResponseForbidden
import re
class SecurityMiddleware:
"""security-focused middleware"""
def __init__(self, get_response):
self.get_response = get_response
self.suspicious_patterns = [
r'<script',
r'javascript:',
r'vbscript:',
r'data:text/html',
r'<iframe',
r'<object',
r'<embed'
]
def __call__(self, request):
# Check for suspicious patterns in request
if self._is_suspicious_request(request):
return HttpResponseForbidden('Suspicious request detected')
response = self.get_response(request)
# Add security headers
response['X-Content-Type-Options'] = 'nosniff'
response['X-Frame-Options'] = 'DENY'
response['X-XSS-Protection'] = '1; mode=block'
response['Referrer-Policy'] = 'strict-origin-when-cross-origin'
return response
def _is_suspicious_request(self, request):
"""Check if request contains suspicious patterns"""
# Check GET parameters
for key, value in request.GET.items():
if self._contains_suspicious_pattern(value):
return True
# Check POST data
if request.method == 'POST':
for key, value in request.POST.items():
if self._contains_suspicious_pattern(value):
return True
# Check headers
for key, value in request.META.items():
if self._contains_suspicious_pattern(str(value)):
return True
return False
def _contains_suspicious_pattern(self, value):
"""Check if value contains suspicious patterns"""
value_str = str(value).lower()
for pattern in self.suspicious_patterns:
if re.search(pattern, value_str):
return True
return False
# 7. Performance Monitoring Middleware
from django.core.cache import cache
import psutil
class PerformanceMiddleware:
"""Performance monitoring middleware"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Record start time and memory
start_time = time.time()
start_memory = psutil.Process().memory_info().rss
# Process request
response = self.get_response(request)
# Calculate metrics
duration = time.time() - start_time
memory_used = psutil.Process().memory_info().rss - start_memory
# Store metrics
self._store_metrics(request.path, duration, memory_used)
# Add performance headers
response['X-Response-Time'] = f"{duration:.3f}s"
response['X-Memory-Used'] = f"{memory_used / 1024:.1f}KB"
return response
def _store_metrics(self, path, duration, memory_used):
"""Store performance metrics"""
cache_key = f"performance_{path}"
metrics = cache.get(cache_key, {
'count': 0,
'total_duration': 0,
'total_memory': 0,
'min_duration': float('inf'),
'max_duration': 0
})
metrics['count'] += 1
metrics['total_duration'] += duration
metrics['total_memory'] += memory_used
metrics['min_duration'] = min(metrics['min_duration'], duration)
metrics['max_duration'] = max(metrics['max_duration'], duration)
cache.set(cache_key, metrics, 3600) # Cache for 1 hour
# 8. 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',
# Custom middleware
'myapp.middleware.JWTAuthMiddleware',
'myapp.middleware.RateLimitMiddleware',
'myapp.middleware.LoggingMiddleware',
'myapp.middleware.CORSMiddleware',
'myapp.middleware.SecurityMiddleware',
'myapp.middleware.PerformanceMiddleware',
]
# 9. Middleware Testing
from django.test import TestCase, RequestFactory
from django.contrib.auth.models import AnonymousUser
class MiddlewareTestCase(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.middleware = JWTAuthMiddleware(lambda r: None)
def test_jwt_authentication(self):
"""Test JWT authentication middleware"""
# Create request with JWT token
request = self.factory.get('/api/posts/')
request.META['HTTP_AUTHORIZATION'] = 'Bearer valid_token'
# Process request
self.middleware(request)
# Check if user is set
self.assertIsNotNone(request.user)
def test_rate_limiting(self):
"""Test rate limiting middleware"""
middleware = RateLimitMiddleware(lambda r: None)
# Create multiple requests
for i in range(6): # Exceed limit of 5
request = self.factory.post('/login/')
response = middleware(request)
if i < 5:
self.assertIsNone(response) # Should pass
else:
self.assertEqual(response.status_code, 429) # Should be rate limited