FastAPI Middleware Order Fix
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
- Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
- Using
headandtailinstead of pattern matching, causing runtime errors on empty lists - 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
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro