Backend Development 20 min read

Essential FastAPI Middleware Guide: Boost Security, Performance, and Functionality

This article explains how FastAPI middleware sits between incoming requests and outgoing responses, detailing built‑in and third‑party middleware such as CORS, GZip, HTTPS redirect, session, trusted host, error handling, rate limiting, authentication, custom headers, logging, timeout, trailing‑slash handling, IP whitelisting, proxy headers, CSRF protection, context management and global state, each with usage scenarios and code examples.

Code Mala Tang
Code Mala Tang
Code Mala Tang
Essential FastAPI Middleware Guide: Boost Security, Performance, and Functionality

Middleware in a web framework acts as a processing layer between incoming requests and outgoing responses, allowing modification of requests, handling of exceptions, session management, response compression, and more. FastAPI supports any ASGI‑compatible middleware, enabling integration of built‑in, Starlette, or custom middleware.

1. CORS (Cross‑Origin Resource Sharing) Middleware

Description

CORS middleware allows or restricts resources on the server to be requested from other domains, which is essential when a FastAPI backend serves a frontend hosted on a different domain.

Use Cases

Enable secure cross‑origin requests in single‑page applications.

Allow API access from different client domains.

Example

<code>from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # allow all origins
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
async def root():
    return {"message": "Hello World"}
</code>

2. GZip Middleware

Description

GZip middleware automatically compresses responses using the GZip algorithm, reducing payload size and improving performance, especially for large JSON or HTML responses.

Use Cases

Provide large JSON responses in RESTful APIs.

Compress static files.

Example

<code>from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddleware

app = FastAPI()

app.add_middleware(GZipMiddleware, minimum_size=1000)  # compress responses larger than 1000 bytes

@app.get("/")
async def root():
    return {"message": "This is a test message that will be compressed."}
</code>

3. HTTPS Redirect Middleware

Description

This middleware automatically redirects all HTTP requests to HTTPS, enforcing secure connections.

Use Cases

Force HTTPS for web applications.

Protect API endpoints by ensuring encrypted traffic.

Example

<code>from fastapi import FastAPI
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware

app = FastAPI()

app.add_middleware(HTTPSRedirectMiddleware)

@app.get("/")
async def root():
    return {"message": "You are being redirected to HTTPS!"}
</code>

4. Session Middleware

Description

Session middleware creates a session ID for each user and stores it in a cookie, allowing stateful interactions across multiple requests.

Use Cases

Manage authentication sessions.

Store temporary data across requests.

Example

<code>from fastapi import FastAPI, Request
from starlette.middleware.sessions import SessionMiddleware

app = FastAPI()

app.add_middleware(SessionMiddleware, secret_key="your-secret-key")

@app.get("/set/")
async def set_session_data(request: Request):
    request.session['user'] = 'john_doe'
    return {"message": "Session data set"}

@app.get("/get/")
async def get_session_data(request: Request):
    user = request.session.get('user', 'guest')
    return {"user": user}
</code>

5. Trusted Host Middleware

Description

Trusted host middleware filters requests to ensure they originate from specified hostnames, preventing HTTP host header attacks.

Use Cases

Prevent DNS rebinding attacks.

Restrict API access to specific hostnames.

Example

<code>from fastapi import FastAPI
from fastapi.middleware.trustedhost import TrustedHostMiddleware

app = FastAPI()

app.add_middleware(TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"])

@app.get("/")
async def root():
    return {"message": "This request came from a trusted host."}
</code>

6. Error Handling Middleware

Description

This custom middleware catches exceptions and returns a unified error response, which can be extended for logging or monitoring.

Use Cases

Custom error logging.

Consistent error responses across the application.

Example

<code>from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware

class ErrorHandlingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        try:
            response = await call_next(request)
        except Exception as e:
            response = JSONResponse({"error": str(e)}, status_code=500)
        return response

app = FastAPI()
app.add_middleware(ErrorHandlingMiddleware)

@app.get("/")
async def root():
    raise ValueError("This is an error!")
</code>

7. Rate Limit Middleware

Description

This middleware limits the number of requests a client can make within a given time window, helping prevent abuse and denial‑of‑service attacks.

Use Cases

Prevent API abuse by throttling request frequency.

Implement tiered usage for public APIs.

Example

<code>from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
import time

class RateLimitMiddleware(BaseHTTPMiddleware):
    def __init__(self, app, max_requests: int, window: int):
        super().__init__(app)
        self.max_requests = max_requests
        self.window = window
        self.requests = {}

    async def dispatch(self, request: Request, call_next):
        client_ip = request.client.host
        current_time = time.time()
        if client_ip not in self.requests:
            self.requests[client_ip] = []
        self.requests[client_ip] = [ts for ts in self.requests[client_ip] if ts > current_time - self.window]
        if len(self.requests[client_ip]) >= self.max_requests:
            return JSONResponse(status_code=429, content={"error": "Too many requests"})
        self.requests[client_ip].append(current_time)
        return await call_next(request)

app = FastAPI()
app.add_middleware(RateLimitMiddleware, max_requests=5, window=60)

@app.get("/")
async def root():
    return {"message": "You haven't hit the rate limit yet!"}
</code>

8. Authentication Middleware

Description

This middleware validates tokens or credentials before a request reaches an endpoint, supporting JWT, OAuth, or custom schemes.

Use Cases

Validate JWT tokens for API requests.

Implement custom authentication logic.

Example

<code>from fastapi import FastAPI, Request, HTTPException
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi.responses import PlainTextResponse

class AuthMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        token = request.headers.get("Authorization")
        if not token or token != "Bearer valid-token":
            return PlainTextResponse(status_code=401, content="Unauthorized")
        return await call_next(request)

app = FastAPI()
app.add_middleware(AuthMiddleware)

@app.get("/secure-data/")
async def secure_data():
    return {"message": "This is secured data"}
</code>

9. Custom Header Middleware

Description

Custom header middleware adds specific headers to every response, such as security headers, caching directives, or custom metadata.

Use Cases

Add authentication tokens or related IDs.

Enhance security with headers like X‑Content‑Type‑Options.

Control client caching behavior.

Example

<code>from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware

class CustomHeaderMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        response.headers['Cache-Control'] = 'public, max-age=3600'
        response.headers['X-Content-Type-Options'] = 'nosniff'
        response.headers['X-Frame-Options'] = 'DENY'
        response.headers['X-XSS-Protection'] = '1; mode=block'
        response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
        return response

app = FastAPI()
app.add_middleware(CustomHeaderMiddleware)

@app.get("/data/")
async def get_data():
    return {"message": "This response is cached for 1 hour."}
</code>

10. Logging Middleware

Description

Logging middleware records each request and response, useful for debugging and monitoring API usage.

Use Cases

Debug issues by tracing request/response cycles.

Monitor API performance.

Example

<code>from fastapi import FastAPI, Request
import logging
from starlette.middleware.base import BaseHTTPMiddleware

logger = logging.getLogger("my_logger")

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

app = FastAPI()
app.add_middleware(LoggingMiddleware)

@app.get("/")
async def root():
    return {"message": "Check your logs for the request and response details."}
</code>

11. Timeout Middleware

Description

Timeout middleware aborts requests that exceed a specified duration, preventing long‑running requests from exhausting server resources.

Use Cases

Prevent resource exhaustion from lengthy requests.

Ensure timely responses in high‑traffic applications.

Example

<code>from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import PlainTextResponse
import asyncio
from starlette.middleware.base import BaseHTTPMiddleware

class TimeoutMiddleware(BaseHTTPMiddleware):
    def __init__(self, app, timeout: int):
        super().__init__(app)
        self.timeout = timeout

    async def dispatch(self, request: Request, call_next):
        try:
            return await asyncio.wait_for(call_next(request), timeout=self.timeout)
        except asyncio.TimeoutError:
            return PlainTextResponse(status_code=504, content="Request timed out")

app = FastAPI()
app.add_middleware(TimeoutMiddleware, timeout=5)

@app.get("/")
async def root():
    await asyncio.sleep(10)  # simulate long processing
    return {"message": "This won't be reached if the timeout is less than 10 seconds."}
</code>

12. Trailing Slash Middleware

Description

This middleware normalizes URLs by adding or removing a trailing slash, ensuring consistent routing and avoiding duplicate routes.

Use Cases

Enforce a uniform URL structure across the app.

Prevent routing issues caused by inconsistent URLs.

Example

<code>from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi.responses import RedirectResponse

class TrailingSlashMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        if not request.url.path.endswith("/"):
            return RedirectResponse(url=f"{request.url.path}/")
        return await call_next(request)

app = FastAPI()
app.add_middleware(TrailingSlashMiddleware)

@app.get("/hello/")
async def hello():
    return {"message": "Hello with trailing slash!"}
</code>

13. IP Whitelist Middleware

Description

IP whitelist middleware restricts access based on the client’s IP address, allowing only requests from specified IPs.

Use Cases

Limit access to internal APIs.

Add an extra security layer by restricting IPs.

Example

<code>from fastapi import FastAPI, Request, HTTPException
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi.responses import PlainTextResponse

class IPWhitelistMiddleware(BaseHTTPMiddleware):
    def __init__(self, app, whitelist):
        super().__init__(app)
        self.whitelist = whitelist

    async def dispatch(self, request: Request, call_next):
        client_ip = request.client.host
        if client_ip not in self.whitelist:
            return PlainTextResponse(status_code=403, content="IP not allowed")
        return await call_next(request)

app = FastAPI()
app.add_middleware(IPWhitelistMiddleware, whitelist=["127.0.0.1", "192.168.1.1"])

@app.get("/")
async def root():
    return {"message": "Your IP is whitelisted!"}
</code>

14. Proxy Headers Middleware

Description

When deploying behind a proxy (e.g., Nginx or an API gateway), this middleware reads proxy‑set headers like X‑Forwarded‑For and X‑Forwarded‑Proto to determine the original client IP and protocol.

Use Cases

Correctly identify client IP behind a proxy.

Handle HTTPS termination at the proxy layer.

Example

<code>from fastapi import FastAPI, Request
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware

app = FastAPI()
app.add_middleware(ProxyHeadersMiddleware)

@app.get("/")
async def root(request: Request):
    return {"client_ip": request.client.host}
</code>

15. CSRF Middleware

Description

CSRFMiddleware adds Cross‑Site Request Forgery protection to FastAPI by generating and validating CSRF tokens, preventing unauthorized commands from being executed on behalf of authenticated users.

Use Cases

Protect forms and actions from CSRF attacks.

Ensure only authenticated users can perform legitimate operations.

Example

<code>pip install starlette-csrf</code>
<code>from fastapi import FastAPI, Request
from starlette_csrf import CSRFMiddleware

app = FastAPI()
app.add_middleware(CSRFMiddleware, secret="__CHANGE_ME__")

@app.get("/")
async def root(request: Request):
    return {"message": request.cookies.get('csrftoken')}
</code>

16. Starlette Context Middleware

Description

starlette‑context is a third‑party middleware that maintains request‑scoped context, allowing storage and retrieval of data (e.g., headers, user info) without passing it through every function.

Use Cases

Attach request‑specific details to logs.

Store metadata needed across different parts of the app.

Track user information throughout the request lifecycle.

Example

<code>pip install starlette-context</code>
<code>from fastapi import FastAPI, Depends
from fastapi.responses import JSONResponse
from starlette_context.middleware import ContextMiddleware
from starlette_context import context

app = FastAPI()
app.add_middleware(ContextMiddleware)

async def set_globals() -> None:
    context["username"] = "chris"

@app.get("/", dependencies=[Depends(set_globals)])
async def info():
    return JSONResponse(context.data)
</code>

17. Globals Middleware

Description

GlobalsMiddleware provides global state management for ASGI applications, similar to Flask’s g object, enabling storage of configuration or request‑specific data accessible throughout the app.

Use Cases

Store global configuration accessible from any component.

Manage request‑specific data without explicit parameter passing.

Example

<code>pip install fastapi-g-context</code>
<code>from fastapi import FastAPI, Depends
from fastapi_g_context import GlobalsMiddleware, g

app = FastAPI()
app.add_middleware(GlobalsMiddleware)

async def set_globals() -> None:
    g.username = "JohnDoe"
    g.request_id = "123456"
    g.is_admin = True

@app.get("/", dependencies=[Depends(set_globals)])
async def info():
    return {
        "username": g.username,
        "request_id": g.request_id,
        "is_admin": g.is_admin,
    }
</code>
backendPythonmiddlewareWeb DevelopmentFastAPI
Code Mala Tang
Written by

Code Mala Tang

Read source code together, write articles together, and enjoy spicy hot pot together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.