Skip to content

Middleware

Middleware is a lightweight plugin system that lets you automatically run custom code during Django's request and response processing cycle. Each piece of middleware performs a specific task, like managing user sessions, handling authentication, or checking for CSRF attacks.

How Middleware Works

Django executes middleware in a specific order defined by the MIDDLEWARE list in your settings file. You can think of this list as layers of an onion.

When a user makes a request, Django applies the middleware layers from the top of the list down to the bottom until it reaches the view. When the view returns a response, Django applies the layers in reverse order, moving from the bottom back up to the top. This means the first middleware in the list is the first to see the request and the last to see the response.

Writing Custom Middleware

You can write your own custom middleware to run specific code on every request. A middleware is simply a callable that takes a request and returns a response. The standard way to write one is by creating a Python class.

The class requires two main methods. The __init__ method runs only once when the server starts. The __call__ method runs once per request.

python
class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization goes here

    def __call__(self, request):
        # Code to be executed for each request BEFORE the view is called
        
        response = self.get_response(request)

        # Code to be executed for each request AFTER the view is called

        return response

Special Middleware Hooks

Besides the basic request and response phases, Django offers special methods you can add to your custom middleware class to handle specific situations.

MethodWhen it runsPurpose
process_view(request, view_func, view_args, view_kwargs)Just before Django calls the viewModifying the request or returning an early response before the view executes.
process_exception(request, exception)When a view raises an exceptionCatching errors and returning a custom error response.
process_template_response(request, response)Just after the view finishes runningModifying a response object that contains a template before it is rendered into HTML.

Dealing with Streaming Responses

Regular HTTP responses contain the entire page content at once. Streaming responses send data in small chunks over time. Because of this difference, a streaming response does not have a content attribute.

If your custom middleware needs to read or modify the response body, it must first check if it is dealing with a streaming response, by checking the streaming property.

python
if response.streaming:
    response.streaming_content = wrap_streaming_content(response.streaming_content)
else:
    response.content = alter_content(response.content)

Asynchronous Support

Django supports both standard synchronous code and modern asynchronous code. By default, Django assumes your custom middleware is synchronous.

If you want your middleware to support asynchronous requests efficiently, you need to tell Django by setting specific class attributes. You set sync_capable = True and async_capable = True on your middleware class to indicate it can handle both styles.

When supporting both modes, your __init__ method must check if the provided get_response function is a coroutine. You then adapt your __call__ method to route the request to either an asynchronous or synchronous processing function.

python
import asyncio
from asgiref.sync import iscoroutinefunction

class AsyncMiddleware:
    sync_capable = True
    async_capable = True

    def __init__(self, get_response):
        self.get_response = get_response
        # Check if the next layer is asynchronous
        self.is_async = iscoroutinefunction(self.get_response)

    def __call__(self, request):
        if self.is_async:
            # Route to the async handler
            return self.__acall__(request)
        
        # Synchronous logic
        response = self.get_response(request)
        return response

    async def __acall__(self, request):
        # Asynchronous logic
        response = await self.get_response(request)
        return response

Activating Middleware

To make Django use your custom middleware, you must add its Python path to the MIDDLEWARE list inside your settings.py file.

IMPORTANT

The order of this list is extremely important because of the top-down and bottom-up execution style. For example, AuthenticationMiddleware relies on SessionMiddleware being loaded first so it can properly track the user's session.

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",
    "myapp.middleware.SimpleMiddleware",  # Your custom middleware
]

Essential Built-in Middleware

Django includes several essential middleware classes by default. Here are the most common ones you will see in a standard project.

MiddlewarePurpose
UpdateCacheMiddlewareWorks together with the fetch middleware to save and update cached versions of your web pages.
FetchFromCacheMiddlewareRetrieves and serves cached web pages to speed up load times and reduce server work.
CommonMiddlewareHandles standard conveniences like blocking forbidden user agents and adding trailing slashes to URLs.
GZipMiddlewareCompresses server responses to reduce file sizes and speed up page loading.
LocaleMiddlewareDetects the user's language preference and translates the website content automatically.
MessageMiddlewareEnables temporary flash messages that can be displayed to the user on the next page load.
SecurityMiddlewareHandles several security enhancements like redirecting HTTP to HTTPS and setting strict transport headers.
SessionMiddlewareManages user sessions across requests so you can store data for specific visitors.
AuthenticationMiddlewareAttaches the currently logged-in user object to every incoming request.
CsrfViewMiddlewareAdds protection against Cross-Site Request Forgery attacks for all POST requests.
XFrameOptionsMiddlewarePrevents other websites from embedding your pages inside a frame to protect against clickjacking attacks.
ContentSecurityPolicyMiddlewareControls where resources like scripts and images can be loaded from to prevent malicious code execution.