Skip to content

FastAPI Middleware Order Fix

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about FastAPI Middleware Order Fix. We cover key concepts, practical examples, and best practices.

The Problem

FastAPI middleware runs in reverse order of registration. This confuses developers who expect first-registered middleware to run first on request.

Quick Fix

Wrong — assuming registration order = execution order

from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware

class MiddlewareA(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        print("A before")
        response = await call_next(request)
        print("A after")
        return response

class MiddlewareB(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        print("B before")
        response = await call_next(request)
        print("B after")
        return response

app = FastAPI()
app.add_middleware(MiddlewareA)  # Registered first
app.add_middleware(MiddlewareB)  # Registered second

Output on request: B before, A before, handler, A after, B after

Middleware runs as a stack: the LAST middleware registered is the OUTERMOST layer.

Correct — understanding the stack order

# The order of wrapping:
# app → MiddlewareB(MiddlewareA(handler))
# Request enters B first, then A, then handler
# Response exits A first, then B

app.add_middleware(MiddlewareA)  # Inner layer
app.add_middleware(MiddlewareB)  # Outer layer (runs first on request)

Proper ordering for common scenarios

from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware

# CORSMiddleware: outermost (runs first on request)
app.add_middleware(TrustedHostMiddleware, allowed_hosts=["*"])
app.add_middleware(GZipMiddleware, minimum_size=1000)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

Output order: CORS → GZip → TrustedHost → handler → TrustedHost → GZip → CORS

Custom middleware with app code

from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request

class TimingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        start = time.time()
        response = await call_next(request)
        elapsed = time.time() - start
        response.headers["X-Process-Time"] = str(elapsed)
        return response

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        print(f"Request: {request.method} {request.url}")
        response = await call_next(request)
        print(f"Response: {response.status_code}")
        return response

# Logging runs first (outer), Timing runs closer to handler (inner)
app.add_middleware(TimingMiddleware)
app.add_middleware(LoggingMiddleware)

Using ASGI middleware directly

from starlette.middleware.base import BaseHTTPMiddleware
from starlette.middleware import Middleware

middleware = [
    Middleware(TrustedHostMiddleware, allowed_hosts=["*"]),
    Middleware(GZipMiddleware, minimum_size=1000),
    Middleware(CORSMiddleware, allow_origins=["*"]),
]

app = FastAPI(middleware=middleware)

Prevention

  • Think of middleware as a stack — last registered = first to process the request.
  • Register critical security middleware (CORS, TrustedHost) last (outermost).
  • Test middleware ordering by observing request/response headers.

Common Mistakes with middleware order

  1. Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
  2. Using head and tail instead of pattern matching, causing runtime errors on empty lists
  3. Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks

These mistakes appear frequently in real-world FASTAPI code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### Why does FastAPI use reverse middleware order?

FastAPI uses Starlette's ASGI middleware pattern. Middleware wraps the app from inside out — the last one added wraps the previous ones.

Can I reorder middleware after registration?

No. Remove and re-add middleware in the desired order. Or use the middleware list parameter in the constructor.

How do I debug middleware order?

Add print/log statements in each middleware's dispatch method to trace the request/response path.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro