Open In App

Middleware: Built-in and Custom in Django

Last Updated : 22 Nov, 2025
Comments
Improve
Suggest changes
1 Likes
Like
Report

Middleware in Django acts as lightweight components placed between the web server and the views. They process requests and responses globally, helping manage common tasks without complicating view code.

  • Wrap reusable functionality, such as CSRF protection, into separate components.
  • Apply automatically to all incoming requests and outgoing responses.
  • Support both class-based and function-based middleware styles.
  • Handle cross-cutting tasks like authentication, security headers, and session management.
  • Useful for global operations such as enforcing HTTPS, compressing responses, or logging performance.

Working of Middleware

Django integrates middleware at multiple stages of the request-response cycle:

httprequest
Django Middleware

1. Request Phase: The request passes through each middleware in the order defined in the MIDDLEWARE setting (top-to-bottom). Middleware can:

  • Inspect or modify the request.
  • Short-circuit processing by returning an HttpResponse early.
  • Return None to continue processing.

2. URL Resolution and View Processing: After passing the middleware stack, the request reaches Django's URL resolver, which executes the matched view function or class-based view to generate a response.

3. Response Phase: The response passes back through the middleware stack in reverse order (bottom-to-top). Middleware can:

  • Modify the response (e.g., add headers).
  • Return a modified response upstream.

4. Exception Handling (Optional):
If an exception occurs during view processing, a process_exception hook in middleware can intervene before the response phase.

This structure ensures that middleware can inject functionality at multiple points in the request-response lifecycle while remaining modular, reusable, and decoupled from individual views.

Middleware Hooks in Django

Hooks are special methods in Django middleware that are called at specific points during the request-response cycle. They allow inspection, modification, or short-circuiting of requests and responses in a modular and reusable way.

  • process_request(request): Runs before the view executes. Can inspect or modify the request. Returning an HttpResponse here stops further processing.
  • process_view(request, view_func, view_args, view_kwargs): Runs after URL resolution but before the view executes. Useful for performing view-specific checks or modifications.
  • process_template_response(request, response): Runs after the view returns a TemplateResponse. Allows adjustments to the template or context before rendering.
  • process_exception(request, exception): Invoked if a view raises an exception. Can handle the exception and return a custom response.
  • process_response(request, response): Runs after the view has returned a response (or after process_exception). Allows final modifications before sending the response back to the client

Types of Middleware in Django

Django's middleware can be divided into 2 types: built-in and custom.

Built-in Django Middleware

Django includes a robust set of built-in middleware classes, enabled by default in new projects. Below is a comprehensive overview including their purposes and key settings:

Middleware ClassPurposeKey Settings
SecurityMiddlewareAdds security headers (e.g., HSTS, X-Content-Type-Options); redirects HTTP to HTTPS.SECURE_HSTS_SECONDS, SECURE_SSL_REDIRECT. Use in production.
SessionMiddlewareEnables session handling; attaches session data to requests.Requires SESSION_ENGINE; place before AuthenticationMiddleware.
CsrfViewMiddlewareProtects against CSRF attacks by validating tokens in POST/PUT/DELETE requests.CSRF_TRUSTED_ORIGINS; exempt views with @csrf_exempt.
XFrameOptionsMiddlewarePrevents clickjacking by setting X-Frame-Options header.X_FRAME_OPTIONS (e.g., 'DENY').
CommonMiddlewareHandles ETags for conditional GETs, redirects for trailing slashes/www, and gzip compression hints.APPEND_SLASH, USE_ETAGS. Place before GZipMiddleware.
AuthenticationMiddlewarePopulates request.user; requires sessions.Place after SessionMiddleware.
MessageMiddlewareAttaches messages (e.g., from messages.success()) to requests for display.Works with django.contrib.messages.
FetchFromCacheMiddlewareRetrieves responses from cache if available (pairs with UpdateCacheMiddleware).Requires caching backend.
UpdateCacheMiddlewareCaches responses post-view.Place at ends of MIDDLEWARE for full coverage.
ConditionalGetMiddlewareSupports HTTP 304 (Not Modified) for ETag/Last-Modified checks.Enhances performance.
GZipMiddlewareCompresses responses for clients that support gzip.Place after CommonMiddleware.
LocaleMiddlewareEnables i18n by setting request.LANGUAGE_CODE from URL/path/cookie.Requires USE_I18N=True.
StaticFilesMiddlewareServes static/media files in development (use collectstatic in production).Only for DEBUG=True; place last.

The default MIDDLEWARE in settings.py includes most of these. Customize as needed, but maintain order for dependencies (e.g., sessions before auth).
Example:

Screenshot-2023-08-18-155432
Sample middleware already included in 'setting.py'

Custom Middleware

Custom middleware allows the implementation of application-specific logic that isn’t covered by Django’s built-in middleware. Django supports two approaches for creating custom middleware:

1. Class-based Middleware

  • Typically inherits from MiddlewareMixin or implements the required hooks directly.
  • Provides clear structure with multiple hooks for process_request, process_view, process_exception, and process_response.
  • Ideal for more complex or reusable middleware components.

2. Function-based Middleware

  • A simple callable that accepts a get_response argument and returns a response.
  • Suitable for lightweight or one-off processing tasks

Implementation Steps:

  1. Create a middleware.py file inside your Django app.
  2. Define the middleware class or function with the desired logic.
  3. Add the middleware to the MIDDLEWARE list in settings.py at the appropriate position in the stack.

Create Custom Middleware in Django

Suppose a Django project has three types of users: Teacher, Student, and Principal. The goal of the middleware is to redirect users to their respective home pages after login:

  • Teacher: Redirected to the teacher’s homepage
  • Student: Redirected to the student’s homepage
  • Principal: Redirected to the principal’s homepage

Setting up the Project

Consider a project  named 'projectmiddleware' having an app named 'testapp'.

Creating Necessary Files

In views.py:

Python
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from testapp.forms import CustomUserCreationForm
from django.contrib.auth import get_user_model
User = get_user_model()
def login_view(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(request, username=username, password=password)
        if user is not None:
            login(request, user)
            # Redirect to home page after login
            return redirect('home')  
    return render(request, 'login.html')

def home_view(request):
    return render(request, 'home.html') 
    
def logout_view(request):
    logout(request)
    # Redirect to login page after logout
    return render(request, 'home.html')  

def signup_view(request):
    if request.method == 'POST':
        print(request.POST['username'])
        print(request.POST['password1'])
        print(request.POST['role'])
        form = CustomUserCreationForm(request.POST)
        print(form.data)
        print(form.errors)
        if form.is_valid():
            print("Valid")
            form.save()
            return redirect('login')
        
    else:
        form = CustomUserCreationForm()
        print("HI")
    return render(request, 'signup.html', {'form': form})
def teacher_home(request):
    print("Welcome Teacher")
    return render(request, 'teacher.html')

def student_home(request):
    print("Welcome Student")
    return render(request, 'student.html')

def principal_home(request):
    print("Welcome Principal")
    return render(request, 'principal.html')

This file manages user authentication, user registration, and user-specific homepages, acting as the central component for handling user interactions.

  • Functions for login and registration
  • Views for teacher_home, student_home, and principal_home
  • Logic to display content based on the logged-in user type

In models.py:

Python
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
    ROLE_CHOICES = (
        ('teacher', 'Teacher'),
        ('student', 'Student'),
        ('principal', 'Principal'),
    )
    
    role = models.CharField(max_length=10, choices=ROLE_CHOICES)

    def __str__(self):
        return self.username

This file defines a custom user model, CustomUser, which extends Django’s AbstractUser. It adds a role field to categorize users as teacher, student, or principal, enabling role-based behavior and access control within the application.

In forms.py:

Python
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import CustomUser

class CustomUserCreationForm(UserCreationForm):
    role = forms.ChoiceField(choices=CustomUser.ROLE_CHOICES, required=True)

    class Meta:
        model = CustomUser
        fields = UserCreationForm.Meta.fields + ('role',)

This Django file defines a custom user creation form, CustomUserCreationForm, which extends UserCreationForm. It includes a role field, allowing users to select their role (teacher, student, or principal) during registration. This ensures consistency with the role-based functionality of the custom user model.

In custom_middleware.py:

Python
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect
from testapp import views
from django.contrib.auth import get_user_model

User = get_user_model()

class CustomMiddleware(MiddlewareMixin):
    def process_request(self, request):
        # Check if the request is for the login or logout views
        if request.path == '/login/':
            # Handle login logic
            print("Login Request")
            # You can perform any additional actions related to login here
            
        elif request.path == '/logout/':
            # Handle logout logic
            print("Logout Request")
            # You can perform any additional actions related to logout here
        elif request.path == '/admin/' :
            print("Admin")
        elif request.user.is_authenticated:
            role = request.user.role
            print(role)
            if role == 'teacher' and not request.path.startswith('/teacher_home'):
                return redirect('teacher_home')
            elif role == 'student' and not request.path.startswith('/student_home'):
                return redirect('student_home')
            elif role == 'principal' and not request.path.startswith('/principal_home'):
                return redirect('principal_home')

        # Continue processing the request

This Django middleware, CustomMiddleware, injects custom logic into the request-processing flow. It intercepts requests for login, logout, and the admin panel, handling them appropriately. For authenticated users, it redirects them to their designated home page based on their role (teacher, student, or principal), ensuring a role-specific navigation experience.

In admin.py:

Python
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from .models import CustomUser

class CustomUserAdmin(UserAdmin):
    list_display = (
        'username', 'email', 'first_name', 'last_name','role'
        )

admin.site.register(CustomUser, CustomUserAdmin)

This Django admin configuration manages the CustomUser model within the admin panel. It customizes the displayed fields and registers the model, allowing administrators to efficiently manage users and their roles.

In urls.py:

Python
from django.contrib import admin
from django.urls import path, include
from testapp import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.login_view, name='login'),
    path('home/', views.home_view, name='home'),
    path('logout/', views.logout_view, name='logout'),
    path('signup/', views.signup_view, name='signup'),
    path('teacher_home/', views.teacher_home, name='teacher_home'),
    path('student_home/', views.student_home, name='student_home'),
    path('principal_home/', views.principal_home, name='principal_home'),    
]

This Django URL configuration maps incoming URLs to the appropriate view functions within the testapp application. It defines routes for admin access, user authentication, and role-based home pages, ensuring that each URL points to its corresponding view logic.

Setting up GUI

home.html: This is a homepage created in HTML.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Welcome to Gfg</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            background-color: #f5f5f5;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 50vh;
        }
        h1 {
            font-size: 24px;
            color: green;
            text-align: center;
            padding: 50px;
            border: 10px solid #ddd;
            background-color: #fff;
            box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
        }
    </style>
</head>
<body>
    <h1>Welcome to Gfg</h1>
</body>
</html>

login.html: This is the login page which is used to collect the credentials from the user and then pass them to the backend.

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Login</title>
</head>
<body>
    <h1>Login</h1>
    <form method="post">
        {% csrf_token %}
        <label for="username">Username:</label>
        <input type="text" name="username">
        <label for="password">Password:</label>
        <input type="password" name="password">
        <button type="submit">Login</button>
    </form>
</body>
</html>

signup.html: This is the signup page which is used to collect the credentials from the user and then register the user.

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Sign Up</title>
</head>
<body>
    <h2>Sign Up</h2>
    <form method="post">
        {% csrf_token %}
        
        <!-- Username Field -->
        <div>
            <label for="{{ form.username.id_for_label }}">Username:</label>
            {{ form.username }}
        </div>
        
        <!-- Password Fields -->
        <div>
            <label for="{{ form.password1.id_for_label }}">Password:</label>
            {{ form.password1 }}
        </div>
        <div>
            <label for="{{ form.password2.id_for_label }}">Confirm Password:</label>
            {{ form.password2 }}
        </div>
        
        <!-- Role Field -->
        <div>
            <label for="{{ form.role.id_for_label }}">Role:</label>
            {{ form.role }}
        </div>
        
        <button type="submit">Sign up</button>
    </form>
</body>
</html>

student.html: This is the homepage for student.

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Student Home</title>
</head>
<body>
    <h2>Welcome, Student!</h2>
    <p>This is the student's home page.</p>
</body>
</html>

teacher.html: This is the homepage for Teacher.

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Teacher Home</title>
</head>
<body>
    <h2>Welcome, Teacher!</h2>
    <p>This is the teacher's home page.</p>
</body>
</html>

principal.html: This is the homepage for principal.

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Principal Home</title>
</head>
<body>
    <h2>Welcome, Principal!</h2>
    <p>This is the principal's home page.</p>
</body>
</html>

urls.py: This Django URL configuration maps URLs to view functions within the testapp application. It defines routes for admin access, user authentication, and role-based home pages, connecting specific URLs to corresponding view functions.

Python
from django.contrib import admin
from django.urls import path, include
from testapp import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.login_view, name='login'),
    path('home/', views.home_view, name='home'),
    path('logout/', views.logout_view, name='logout'),
    path('signup/', views.signup_view, name='signup'),
    path('teacher_home/', views.teacher_home, name='teacher_home'),
    path('student_home/', views.student_home, name='student_home'),
    path('principal_home/', views.principal_home, name='principal_home'),    
]

Deploying the Project:

Apply migrations to set up the database:

python manage.py makemigrations

python manage.py migrate

Start the development server:

python3 manage.py runserver

This will launch the Django application locally, allowing you to access it via the default URL http://127.0.0.1:8000/.

Output Video:

ezgifcom-optimize


Middleware in Django
Visit Course explore course icon

Explore