Skip to main content

Django Middleware Introduction

In this tutorial, you'll learn about Django middleware — one of Django's most powerful yet often misunderstood features. Middleware provides a way to process requests and responses globally throughout your Django application.

What is Middleware?​

Middleware in Django is a lightweight, low-level plugin system for globally altering Django's input or output. Each middleware component is responsible for doing some specific function. For example, Django includes middleware that:

  • Handles session management
  • Authenticates users
  • Secures requests with CSRF protection
  • And much more!

Think of middleware as a series of "filters" that each request or response must pass through before reaching the view or the client.

How Middleware Works​

When a client makes a request to a Django application, the request passes through all the middleware listed in your MIDDLEWARE setting (from top to bottom) before reaching the view. Once the view processes the request and returns a response, the response passes back through all the middleware (from bottom to top) before being sent to the client.

Middleware Flow

Request/Response Cycle​

  1. Client sends a request to the server
  2. The request passes through all middleware (top to bottom)
  3. The view processes the request
  4. The view returns a response
  5. The response passes through all middleware (bottom to top)
  6. The response is sent back to the client

Default Middleware in Django​

Django comes with several built-in middleware components. Here's a look at the default MIDDLEWARE setting:

python
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',
]

Let's explore what some of these do:

  • SecurityMiddleware: Handles several security enhancements
  • SessionMiddleware: Enables session support
  • CommonMiddleware: Handles common request processing like URL rewriting
  • CsrfViewMiddleware: Adds protection against Cross Site Request Forgeries
  • AuthenticationMiddleware: Associates users with requests using sessions
  • MessageMiddleware: Enables cookie and session-based message support
  • XFrameOptionsMiddleware: Protects against clickjacking via the X-Frame-Options header

Creating Your First Middleware​

Now, let's create a simple custom middleware. We'll make one that logs the time it takes to process each request.

Step 1: Define the Middleware Class​

Create a new file called middleware.py in one of your Django app directories:

python
import time
import logging

logger = logging.getLogger(__name__)

class RequestTimingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization

def __call__(self, request):
# Code to be executed before the view (and other middleware) is called
start_time = time.time()

response = self.get_response(request)

# Code to be executed after the view is called
duration = time.time() - start_time
logger.info(f"Request to {request.path} took {duration:.2f}s")

return response

Step 2: Add the Middleware to Settings​

Add your middleware to the MIDDLEWARE list in your settings.py file:

python
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',
'myapp.middleware.RequestTimingMiddleware', # Add your middleware here
]

Now, every request will be timed, and the duration will be logged.

Middleware Hook Methods​

Besides the __call__ method, middleware can define several other specialized methods:

process_view(request, view_func, view_args, view_kwargs)​

Called just before Django calls the view. Returns either None or an HttpResponse object.

process_exception(request, exception)​

Called when a view raises an exception. Returns either None or an HttpResponse object.

process_template_response(request, response)​

Called just after the view has finished executing, if the response has a render() method.

Let's update our middleware to use the process_exception hook:

python
class RequestTimingMiddleware:
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
logger.info(f"Request to {request.path} took {duration:.2f}s")
return response

def process_exception(self, request, exception):
logger.error(f"Exception occurred while processing {request.path}: {str(exception)}")
return None # We're not handling the exception, just logging it

Real-World Use Case: IP-Based Access Restriction​

Let's create a practical middleware that restricts access to certain views based on IP address:

python
class IPRestrictionMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# Define allowed IPs for restricted areas
self.restricted_paths = ['/admin/', '/api/']
self.allowed_ips = ['127.0.0.1', '192.168.1.100']

def __call__(self, request):
# Check if the path is in restricted areas
if any(request.path.startswith(path) for path in self.restricted_paths):
# Get client's IP
ip = self.get_client_ip(request)
if ip not in self.allowed_ips:
from django.http import HttpResponseForbidden
return HttpResponseForbidden("Access denied based on your IP address.")

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:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip

Add this middleware to your MIDDLEWARE setting, and it will restrict access to the admin and API areas to only the specified IP addresses.

Best Practices for Middleware​

  1. Keep it simple: Middleware should do one thing and do it well
  2. Make it efficient: Since middleware runs on every request, keep it lightweight
  3. Order matters: Understand the order of middleware execution
  4. Be careful with exceptions: Properly handle exceptions to avoid breaking the application
  5. Test thoroughly: Test your middleware with different scenarios

Summary​

Django middleware is a powerful system that allows you to process requests and responses globally throughout your application. You've learned:

  • What middleware is and how it works
  • The request/response cycle in Django
  • Default middleware included in Django
  • How to create custom middleware
  • Advanced middleware features like hook methods
  • A real-world example of IP-based access restriction

By leveraging middleware, you can add global functionality to your Django application without modifying individual views.

Additional Resources​

Exercises​

  1. Create a middleware that adds a custom header to every response
  2. Build a middleware that logs all POST requests to a file
  3. Implement a middleware that detects and blocks potential bot traffic based on user agent
  4. Create a middleware that measures database query count and time for each request
  5. Develop a rate-limiting middleware that restricts the number of requests per minute from a single IP

Happy coding with Django middleware!



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)