15 Useful Middlewares for FastAPI That You Should Know About đ
These are the middlewares I reach for when shipping real-world FastAPI projects.

FastAPI is already fastâââbut these middlewares make it cleaner, safer, and production-ready.
15 Useful Middlewares for FastAPI That You Should Know About đ
FastAPI has rapidly become the go-to framework for building high-performance APIs in Python. With its async-first approach, auto-generated docs, and seamless Pydantic integration, itâs no surprise developers love it.
But what really takes a FastAPI project from good to great?
Middleware.
Middlewares allow you to process requests before they reach your routes and responses before theyâre returned to the client. Theyâre perfect for tasks like authentication, logging, error handling, rate limiting, and much more.
In this article, Iâve curated 15 useful FastAPI middlewaresâââboth built-in and community-poweredâââthat can seriously level up your backend game.
1. BaseHTTPMiddleware (Built-in)
Use this when you want full control over what happens before and after every request in your FastAPI app.
BaseHTTPMiddleware
is part of Starlette, the ASGI toolkit that FastAPI is built on top of.
from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware
app = FastAPI()
class CustomHeaderMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
# đš Before request
print(f"Request URL: {request.url}")
# Call the next middleware or route
response = await call_next(request)
# đš After response
response.headers["X-Custom-Header"] = "MyValue"
return response
# Add middleware to app
app.add_middleware(CustomHeaderMiddleware)
@app.get("/")
async def read_main():
return {"message": "Hello World"}
Perfect for custom logic like header injection or user tracking.
2. CORS Middleware
CORS (Cross-Origin Resource Sharing) is a mechanism that allows your frontend JavaScript app (on one domain) to securely interact with your FastAPI backend (on another domain).
By default, browsers block requests made from a frontend domain (like http://localhost:3000
) to a backend domain (like http://localhost:8000
) due to security reasons (Same-Origin Policy).
This becomes a problem in almost every real-world app where:
- You build a frontend in React, Vue, or Angular
- Your API runs on a different domain, subdomain, or port
- You want them to talk to each other
Without proper CORS configuration, youâll see errors like:
Access to fetch at 'http://localhost:8000/data' from origin 'http://localhost:3000' has been blocked by CORS policy
FastAPIâs Solution: CORSMiddleware
FastAPI (via Starlette) provides a built-in CORS middleware that makes this super easy to configure.
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# Allowed origins (can be specific URLs or wildcards)
origins = [
"http://localhost",
"http://localhost:3000", # your frontend dev server
"https://yourfrontenddomain.com"
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # allowed frontend origins
allow_credentials=True, # allow cookies, headers, sessions
allow_methods=["*"], # allow all HTTP methods
allow_headers=["*"], # allow all headers
)
CORS is one of those things that just works once itâs configured properlyâââbut can cause major headaches when itâs not.
Set it up early, test it often, and keep it tight in production.
3. GZipMiddleware
Use GZipMiddleware to compress your FastAPI responses and significantly reduce the amount of data transferred over the networkâââespecially helpful for large JSON payloads.
Modern browsers and HTTP clients support GZip compression natively. When your server sends compressed responses, the client automatically decompresses them, saving:
- Bandwidth
- Latency
- Load time
This is critical for APIs that return large datasets (like lists of users, search results, or analytics reports).
FastAPI includes built-in GZip support through Starletteâs GZipMiddleware
.
Hereâs how easy it is to use:
from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddleware
app = FastAPI()
# Add GZip middleware
app.add_middleware(GZipMiddleware, minimum_size=1000)
If youâre building a public-facing or data-heavy API, not using GZip is like sending your data through a traffic jam when you could fly it in a drone.
So turn it onâââitâs free performance.
4. TrustedHostMiddleware
Use this middleware to protect your FastAPI app against Host header attacks, like DNS rebinding.
When a request hits your API, it includes a Host
header (e.g., Host: api.example.com
) to specify which domain the client is targeting.
If your app doesnât validate that header, attackers can send requests with a fake Host
, potentially tricking your app into:
- Generating poisoned links or redirects
- Creating unsafe URL references
- Returning misleading error messages or content
This can lead to DNS rebinding attacks, especially in internal apps or cloud deployments.
It allows you to whitelist the domains your app should respond to. If a request comes in with an unknown Host
headerâââit gets rejected with a 400 Bad Request
.
from fastapi import FastAPI
from starlette.middleware.trustedhost import TrustedHostMiddleware
app = FastAPI()
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=[
"example.com", # main domain
"*.example.com", # all subdomains
"localhost", # for local dev
"127.0.0.1" # also valid for dev
]
)
Security isnât just about passwords and firewalls. Sometimes, a single unvalidated header can be the weakest link.
Adding TrustedHostMiddleware
is a simple step with big defensive valueâââespecially for public APIs, dashboards, or any web-exposed service.
5. Request Logging Middleware
Logs every incoming HTTP request and outgoing response, helping you trace errors, analyze traffic, and monitor system behavior in real time.
- Debugging: Understand exactly what the client sent and what the API responded with.
- Monitoring: Track response times, endpoints usage, and traffic patterns.
- Security auditing: Identify suspicious activity or unexpected usage.
- Troubleshooting: Quickly spot failed requests or long-running endpoints.
Basic Implementation Using BaseHTTPMiddleware
from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware
import time
import logging
# Configure basic logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI()
class LoggingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
start_time = time.time()
# Log incoming request
logger.info(f"âĄď¸ {request.method} {request.url.path}")
response = await call_next(request)
process_time = round((time.time() - start_time) * 1000, 2)
# Log response status and duration
logger.info(f"âŹ
ď¸ {request.method} {request.url.path} - {response.status_code} ({process_time} ms)")
return response
# Add to app
app.add_middleware(LoggingMiddleware)
@app.get("/hello")
async def hello():
return {"message": "Hello, world!"}
Example Log Output
âĄď¸ GET /hello
âŹ
ď¸ GET /hello - 200 (1.43 ms)
Logs are your backendâs diary.
Every bug, crash, slow endpoint, or breach has likely left a traceâââif youâre logging right.
Start with simple request/response logs. Then level up with structured logging, trace IDs, and observability tools.
6. SlowAPI (Rate Limiting)
Use SlowAPI to prevent abuse, brute-force attacks, and API overuse by limiting how often a client (usually by IP) can hit your endpoints.
Without rate limiting, your API is vulnerable to:
- Spamming (accidental or intentional)
- Brute-force attacks (e.g., login endpoints)
- Cost spikes (especially with 3rd-party APIs or cloud functions)
- Server overloads and denial of service
SlowAPI gives you a clean and flexible way to limit how many requests a client can makeâââbased on IP, headers, or custom keys.
Installing SlowAPI
pip install slowapi
Basic Usage
from fastapi import FastAPI, Request
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
app = FastAPI()
# Initialize Limiter
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
# Add exception handler
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
@app.get("/limited")
@limiter.limit("5/minute")
async def limited_endpoint(request: Request):
return {"message": "This is a rate-limited endpoint"}
Try It with curl
curl http://localhost:8000/limited
Hit it 5 times quicklyâââon the 6th, youâll get:
429 Too Many Requests
SlowAPI is one of those tools thatâs easy to implement but has massive security and reliability benefits.
Donât wait until your API is under fireâââset up sensible limits today.
7. Session Middleware (Starlette)
SessionMiddleware allows you to store user-specific data on the server (like login state, user preferences, or cart items) while tracking users via signed cookies.
Even though modern APIs often use JWTs or OAuth for stateless auth, sessions still make sense when:
- Youâre building form-based authentication
- You want user sessions stored securely on the server
- Youâre working on admin dashboards or legacy-style web apps
- You want to avoid exposing sensitive data in tokens
Basic Setup in FastAPI
from fastapi import FastAPI, Request
from starlette.middleware.sessions import SessionMiddleware
app = FastAPI()
# Add session middleware
app.add_middleware(
SessionMiddleware,
secret_key="super-secret-key", # đ used for signing the cookie
session_cookie="myapp_session", # optional: custom cookie name
max_age=86400 # optional: cookie expiration in seconds (1 day)
)
@app.get("/set-session")
async def set_session(request: Request):
request.session["username"] = "aashish"
return {"message": "Session set"}
@app.get("/get-session")
async def get_session(request: Request):
username = request.session.get("username", "Guest")
return {"username": username}
Example Use Case: Login System
@app.post("/login")
async def login(request: Request):
# Assume user is authenticated
request.session["user_id"] = "12345"
return {"message": "Logged in"}
@app.get("/me")
async def get_current_user(request: Request):
user_id = request.session.get("user_id")
if not user_id:
return {"message": "Not logged in"}
return {"user_id": user_id}
If youâre building a stateful web application in FastAPIâââand you want simple, secure user session management without extra infrastructureâââSessionMiddleware is the perfect fit.
Fast, simple, and zero dependencies beyond FastAPI and Starlette.
8. CORSMiddleware + Custom Preflight Caching
Improve frontend performance by caching CORS preflight responses using custom headersâââreducing unnecessary network calls.
Whenever a browser makes a cross-origin request with:
- A custom header (e.g.,
Authorization
) - A method like
PUT
,PATCH
, orDELETE
- Cookies or credentials
âŚit first sends a preflight request:
OPTIONS /endpoint
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization
This checks if the actual request is allowed, based on your CORS rules.
If approved, the browser proceeds with the main request.
Problem: Too Many OPTIONS Requests
Browsers send a preflight OPTIONS request every timeâââunless told otherwise.
This causes:
- Increased latency
- Redundant round-trips
- Wasted backend resources
The Fix: Custom Preflight Caching
Tell the browser:
âHey, this preflight result is valid for a whileâââno need to ask again.â
We do this by setting the Access-Control-Max-Age
header.
Built-in CORSMiddleware
+ Caching
FastAPIâs CORS middleware (from Starlette) doesnât expose this config directly. So we need to wrap or extend it to inject our custom headers.
Custom CORSMiddleware With Max-Age
from fastapi import FastAPI, Request, Response
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.base import BaseHTTPMiddleware
app = FastAPI()
# Step 1: Add default CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Step 2: Create a custom middleware to set Access-Control-Max-Age
class PreflightCacheMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
if request.method == "OPTIONS":
response = await call_next(request)
# đ§ Tell the browser to cache the preflight response for 10 minutes
response.headers["Access-Control-Max-Age"] = "600"
return response
return await call_next(request)
app.add_middleware(PreflightCacheMiddleware)
Test With curl
curl -X OPTIONS http://localhost:8000/api \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Authorization"
You should see:
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Max-Age: 600
If youâre using FastAPI to power a modern frontend (like React, Vue, or Angular), adding preflight caching is a low-effort, high-impact optimization.
It makes your app feel fasterâââand scales more efficiently under load.
9. Sentry Middleware (Error Monitoring)
Catch exceptions, monitor performance, and get notified instantly when your FastAPI app crashesâââusing Sentry.
Sentry is a powerful error tracking and application monitoring platform. It helps you:
- Trace errors and stack traces
- Monitor performance metrics (latency, throughput)
- Connect errors to code commits, releases, and users
- Get real-time alerts when things break
FastAPI apps are often async, API-driven, and built for scale. That means:
- Bugs can be harder to trace
- Performance bottlenecks can go unnoticed
- Uptime becomes critical
Sentry + FastAPI gives you observability âfor freeââââall in one dashboard.
Installation
pip install --upgrade sentry-sdk
Basic Integration with Middleware
import sentry_sdk
from fastapi import FastAPI
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
# Initialize Sentry with your DSN
sentry_sdk.init(
dsn="https://your-sentry-dsn@o0.ingest.sentry.io/0",
traces_sample_rate=1.0, # enable full performance tracing (adjust in prod)
environment="production", # optional
release="myapp@1.0.0", # optional
)
app = FastAPI()
# Add Sentry middleware
app.add_middleware(SentryAsgiMiddleware)
@app.get("/")
def root():
return {"message": "Hello Sentry"}
@app.get("/error")
def trigger_error():
division_by_zero = 1 / 0
return {"message": "This line will not be reached"}
Custom Error Context
You can attach extra context, like user data or tags:
from sentry_sdk import set_user, set_tag
@app.middleware("http")
async def enrich_sentry(request, call_next):
set_user({"id": "user_123", "email": "aashish@example.com"})
set_tag("env", "production")
response = await call_next(request)
return response
Advanced Config Options
sentry_sdk.init(
dsn="https://your-dsn",
environment="staging",
traces_sample_rate=0.2, # sample 20% of requests
send_default_pii=True, # send user details (only if you want!)
)
Test Your Setup
curl http://localhost:8000/error
Visit your Sentry dashboard â youâll see the exception with full traceback, request info, and environment.
Monitor Background Tasks Too
If youâre using FastAPI with Celery, RQ, or asyncio background jobs, you can manually capture errors:
from sentry_sdk import capture_exception
try:
risky_operation()
except Exception as e:
capture_exception(e)
In production, âno logsâ â âno bugs.â
Sentry gives you that critical observability layerâââso you can catch, debug, and fix problems before your users notice.
10. Prometheus Middleware (Metrics)
Expose rich metrics from your FastAPI appâââlike request counts, error rates, and latencyâââusing Prometheus and visualize them using tools like Grafana.
Prometheus helps you answer questions like:
- How many requests is my app serving per second?
- Which endpoints are slow?
- How often are errors happening?
- Whatâs the average latency per route?
All of this makes it easier to:
- Diagnose issues in real-time
- Trigger alerts on anomalies
- Monitor performance trends
- Ensure SLAs are met
Install Required Packages
pip install prometheus-fastapi-instrumentator
Quickstart: Adding Prometheus Middleware
from fastapi import FastAPI
from prometheus_fastapi_instrumentator import Instrumentator
app = FastAPI()
# Create instrumentator instance
instrumentator = Instrumentator().instrument(app).expose(app)
@app.get("/ping")
async def ping():
return {"message": "pong"}
Thatâs it! Your metrics are now being tracked and exposed at:
GET /metrics
Customizing the Middleware
You can customize how metrics are grouped and labeled:
from prometheus_fastapi_instrumentator import Instrumentator
instrumentator = Instrumentator(
should_group_status_codes=True,
should_ignore_untemplated=True,
should_respect_env_var=True,
env_var_name="ENABLE_METRICS",
)
instrumentator.instrument(app).expose(app, include_in_schema=False)
Example Metrics Output
# HELP http_request_duration_seconds Histogram of response duration
http_request_duration_seconds_bucket{method="GET",handler="/ping",status="200",le="0.1"} 12.0
http_request_duration_seconds_sum{method="GET",handler="/ping",status="200"} 1.45
http_request_duration_seconds_count{method="GET",handler="/ping",status="200"} 12
These metrics can be scraped by Prometheus on a schedule.
Prometheus Config Example
scrape_configs:
- job_name: 'fastapi-app'
static_configs:
- targets: ['localhost:8000']
Make sure your FastAPI app is running and /metrics
is reachable.
If youâre serious about uptime, performance, and reliability, Prometheus should be your appâs telemetry backbone.
With just a few lines of code, FastAPI + Prometheus gives you production-grade monitoring.
11. Cache Middleware
Use caching to reduce redundant processing and database queriesâââspeeding up your FastAPI endpoints dramatically.
Imagine you have:
- A product listing endpoint that doesnât change every second
- A public API serving weather or stock info
- A report-heavy dashboard querying huge datasets
Instead of recalculating the response every time, you store and reuse itâââcutting latency and server load.
Install FastAPI Cache
pip install fastapi-cache2[redis]
Basic Setup with Redis
from fastapi import FastAPI
from fastapi_cache2 import FastAPICache
from fastapi_cache2.backends.redis import RedisBackend
import redis.asyncio as redis
app = FastAPI()
@app.on_event("startup")
async def startup():
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
FastAPICache.init(RedisBackend(r), prefix="fastapi-cache")
Add Caching to Endpoints
from fastapi_cache2.decorator import cache
@app.get("/products")
@cache(expire=60) # cache for 60 seconds
async def get_products():
# Simulate DB or computation
await asyncio.sleep(1)
return {"products": ["Laptop", "Phone", "Tablet"]}
Now repeated calls within 60 seconds return instantly.
Output Comparison
First request:
GET /products
âąď¸ Took 1000ms
Second request:
GET /products
⥠Took ~1ms (from cache)
Advanced Options
@cache(expire=300, namespace="reports", key_builder=my_custom_key_func)
expire
: TTL in secondsnamespace
: Logical grouping of cache entrieskey_builder
: Custom cache key function
You can even invalidate the cache manually if needed:
await FastAPICache.clear(namespace="reports")
In the world of APIs, speed is user experience.
By caching even a handful of high-traffic endpoints, you can cut latency, save compute, and scale effortlessly.
12. SQLAlchemy Session Middleware
Automatically create and teardown a SQLAlchemy session per requestâââthe cleanest way to manage database access in FastAPI.
Without middleware, you often do this manually:
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
While this works, it has downsides:
- Repetitive across routes
- Manual lifecycle handling
- Easy to forget
.close()
, leading to leaked DB connections
First, Set Up SQLAlchemy
pip install sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
Base = declarative_base()
Create Middleware
Hereâs a simple custom SQLAlchemySessionMiddleware
:
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
from sqlalchemy.exc import SQLAlchemyError
class SQLAlchemySessionMiddleware(BaseHTTPMiddleware):
def __init__(self, app, db_session_factory):
super().__init__(app)
self.db_session_factory = db_session_factory
async def dispatch(self, request: Request, call_next):
request.state.db = self.db_session_factory()
try:
response = await call_next(request)
request.state.db.commit()
except SQLAlchemyError:
request.state.db.rollback()
raise
finally:
request.state.db.close()
return response
Add to FastAPI App
from fastapi import FastAPI
app = FastAPI()
app.add_middleware(SQLAlchemySessionMiddleware, db_session_factory=SessionLocal)
Accessing the DB Session in Routes
Now you can use request.state.db
directly:
from fastapi import Request
@app.get("/users")
def read_users(request: Request):
db = request.state.db
users = db.query(User).all()
return users
Manually managing SQLAlchemy sessions in every route quickly becomes unscalable.
With SQLAlchemy Session Middleware, you get clean request isolation, less boilerplate, and rock-solid DB hygiene.
13. JWT Authentication Middleware
Use middleware to automatically validate JWTs and attach the authenticated user to the requestâââwithout repeating auth logic in every route.
FastAPIâs built-in Depends()
system is great, but when your app grows, using JWT validation in middleware offers:
- Centralized logic
- Cleaner routesâââno repetitive
Depends(get_current_user)
- Seamless request injection (e.g.,
request.state.user
)
Dependencies
pip install python-jose
Weâll use python-jose
to decode JWTs.
Setup: JWT Utility Functions
from jose import JWTError, jwt
from datetime import datetime, timedelta
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
def decode_jwt(token: str):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except JWTError:
return None
Custom Middleware for JWT Auth
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi import Request, HTTPException, status
class JWTAuthMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
auth_header = request.headers.get("Authorization")
if auth_header:
try:
scheme, token = auth_header.split()
if scheme.lower() != "bearer":
raise ValueError("Invalid scheme")
payload = decode_jwt(token)
if payload is None:
raise ValueError("Invalid or expired token")
# Attach user info to request state
request.state.user = payload
except ValueError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
else:
request.state.user = None # Anonymous request
return await call_next(request)
Add Middleware to FastAPI
from fastapi import FastAPI
app = FastAPI()
app.add_middleware(JWTAuthMiddleware)
Access Authenticated User in Routes
from fastapi import Request
@app.get("/me")
async def get_me(request: Request):
user = request.state.user
if not user:
raise HTTPException(status_code=401, detail="Not authenticated")
return {"user": user}
Using JWT middleware in FastAPI keeps your codebase cleaner, DRY, and more secureâââespecially for large-scale or enterprise apps.
14. Request ID Middleware
Automatically attach a unique ID to every incoming requestâââand use it to trace logs, debug errors, and correlate services in distributed systems.
Step 1: Install python-uuid
(Optional)
FastAPI doesnât need external packages for UUIDs, but you can use ulid-py
or uuid
from the standard lib.
pip install ulid-py # (optional)
Step 2: Custom Middleware for Request IDs
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from uuid import uuid4
class RequestIDMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
request_id = request.headers.get("X-Request-ID", str(uuid4()))
request.state.request_id = request_id # store it in request context
response = await call_next(request)
response.headers["X-Request-ID"] = request_id # propagate to response
return response
Step 3: Add to Your FastAPI App
from fastapi import FastAPI
app = FastAPI()
app.add_middleware(RequestIDMiddleware)
Now every request has a unique IDâââeither provided by the client (X-Request-ID
) or generated server-side.
Step 4: Use in Logs or Debugging
from fastapi import Request
@app.get("/debug")
async def debug(request: Request):
print(f"Request ID: {request.state.request_id}")
return {"request_id": request.state.request_id}
Logs now contain context like:
[INFO] [Request ID: 5a02a3fd-21a1-4ccf-a91f-d82143e3a00c] GET /debug
If youâve ever struggled to debug âthat one weird bug in production,â
Request ID Middleware is your secret weapon.
Itâs small, simple, and makes observability 10x betterâââespecially in distributed or containerized environments.
15. Helmet Middleware (Security Headers)
Add essential HTTP headers to secure your FastAPI app against common vulnerabilities like XSS, clickjacking, and MIME sniffing.
In the Node.js world, Helmet is a middleware that automatically sets secure headers like:
X-Content-Type-Options
X-Frame-Options
Strict-Transport-Security
Content-Security-Policy
Referrer-Policy
FastAPI doesnât have built-in support like Helmet, but we can easily mimic its functionality using custom middleware.
Create Helmet-Like Middleware in FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
class HelmetMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
response: Response = await call_next(request)
# Add security headers
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Referrer-Policy"] = "no-referrer"
response.headers["Permissions-Policy"] = "geolocation=(), camera=()"
response.headers["Strict-Transport-Security"] = "max-age=63072000; includeSubDomains; preload"
response.headers["Content-Security-Policy"] = (
"default-src 'self'; "
"script-src 'self'; "
"style-src 'self'; "
"img-src 'self'; "
"font-src 'self'; "
)
return response
Add the Middleware to FastAPI
from fastapi import FastAPI
app = FastAPI()
app.add_middleware(HelmetMiddleware)
Now, every response includes secure headers by default.
Example Output Headers
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: no-referrer
Permissions-Policy: geolocation=(), camera=()
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Content-Security-Policy: default-src 'self'
Most attacks on web apps exploit misconfigured headers.
With Helmet-style middleware, you patch entire classes of vulnerabilitiesâââin a single place.
Itâs lightweight, powerful, and should be in every FastAPI project that touches the web.
Wrapping Up
FastAPIâs middleware system is powerful and flexible. Whether youâre building a small internal API or scaling a high-traffic SaaS product, middlewares help you standardize behaviors and enforce policies across your app.
Start smallâââadd logging and CORS. Then layer in security, rate-limiting, and observability as your app matures.
Let me know in the comments:
Which middleware do you always include in your FastAPI projects?
If you enjoyed this, follow me for more FastAPI and Python contentâââI drop practical tips weekly!
Like, clap, and share if this helped you.
