Let's talk about Middleware in Django, why use them, how it works, and how to create custom middleware in Django.
When I started working with Django, the concept of middleware sounded abstract to me. But I soon discovered that it is one of the most powerful tools for controlling what happens between a user's request and the server's response. In this article, I explain what middlewares are in Django, how they work, and how to create your own cleanly and efficiently.
In Django, middleware is a lightweight plugin that is processed during the execution of requests and responses.
Middleware is used to perform a function in the application. The functions can be security, session, CSRF protection, authentication, etc.
Middlewares in Django: what they are, how they work, and how to create a custom one
The term middleware usually sounds abstract in any development we are going to do, something advanced or complete, but it is not; a Middleware is simply an intermediary, a function we want to execute BEFORE (or after) certain user requests, for example, to verify that the user is authenticated BEFORE entering a resource in our app...
In this article, I explain what middlewares are in Django, how they work, and how to create your own cleanly and efficiently.
1. What a middleware is in Django and what it is for
A middleware is a piece of code that runs between the user's browser and the Django view.
Its mission is to intercept the request before it reaches the view, or process the response before sending it back.
Some examples of use:
- Security: apply protection rules (CSRF, clickjacking, authentication).
- Sessions and users: handle cookies or session data.
- Monitoring and performance: register response times.
- Logs or auditing: save information about each request.
2. How the middleware cycle works in Django
The life cycle of a middleware follows a very clear flow:
- Django receives a request.
- Each middleware processes it in order, from top to bottom (according to the order defined in settings.py).
- The view is executed.
- The response passes back through the middlewares, this time in reverse order.
This means that the order matters: if you define an authentication middleware after one that needs an authenticated user, you will have errors.
3. Types of middlewares in Django: integrated and custom
There are two types of middleware in Django:
- Built-in middleware
- Custom middleware
It already includes several useful middlewares by default, which are defined in settings.py:
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',
]The built-in middleware is provided by default in Django when you create your project. You can check the default Middleware in your project's settings.py file.
Creating your own custom middleware
You can write your own custom Middleware for your project. Custom middleware can be function-based or class-based. This is useful when:
- Running an action on all requests (e.g., logging IPs).
- Applying a global business rule (example: checking if the user completed their profile).
- Measuring performance or adding custom headers.
4. How to create a custom middleware step-by-step
Let's see how to do it in two versions: function and class.
Function-based Middleware
def simple_middleware(get_response):
# One-time configuration and initialization.
def middleware(request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = get_response(request)
# Code to be executed for each request after
# the view are called.
return response
return middlewareClass-based Middleware
class CustomMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
print("custom middleware before next middleware/view")
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request after
# the view are called.
print("custom middleware after middleware/view")
return responseOnce you have written the middleware code, you will have to plug it into your Django request/response flow. You must add the entry to the MIDDLEWARE section of the setting.py file.
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',
# your custom middleware
'your_app.custom_middleware_file.CustomMiddleware_class',
]Remember to add the middleware in an appropriate position, since the middleware evaluation depends on the position; that is, in Django, it will first execute those you define first, precisely in the order in which they are defined.
Things to remember when using middleware
- The order of the middleware is important.
- A middleware can implement process_request but cannot implement process_response and process_view
- Conclusion
- Custom middleware should be used when you want to implement certain code for every request or response, or both.
- Write custom middlewares in Django only when you have unique requirements to employ them, as it means additional overhead to the request-response cycle that, at certain times, can have a negative effect.
5. Good practices and common errors when using middlewares in Django
- Be careful with the order.
The first ones on the list process the request earlier; the last ones process the response before sending it. - Avoid making heavy queries.
Middlewares run on every request: don't place costly logic or unnecessary database access. - Handle exceptions correctly.
Use process_exception to catch errors and return custom responses. - Test your middleware.
Run tests with pytest or Django's test runner to ensure it doesn't break the flow.
6. Conclusion and final tips
Middlewares in Django are a powerful tool for controlling and enriching the request and response cycle.
They allow you to centralize cross-cutting logic (security, auditing, performance) without duplicating code in the views.
In summary:
- Use built-in middlewares whenever they cover your needs.
- Create custom middlewares only when required.
- Measure the performance impact before implementing them in production.
- And, above all, experiment. Understanding well how a request travels in Django gives you full control over your application.
Quick FAQs
What is the difference between class-based and function-based middleware?
Both do the same; class-based is more scalable and allows for the use of additional hooks.
Can I have multiple custom middlewares?
Yes, and they run in order according to their position in the MIDDLEWARE list.
What common errors are made?
Forgetting to return a response, altering the order of middlewares, or executing costly queries within them.
I agree to receive announcements of interest about this Blog.
We'll learn what middleware is, how to use it, which types exist and the different types in Django, as well as some tips for creating your own.