A practical guide to implementing middleware in FastAPI for better performance, security, and efficiency
In this article, we'll explore how to add middleware to a FastAPI application. We'll go through three examples: adding a process time header to track how long requests take, setting up security headers to keep your app safe, and using GzipMiddleware to compress responses for faster delivery. Plus, we'll talk about why the order of these middlewares matters.
Middleware in FastAPI is a function that runs for every request and response. It takes the incoming request, does something with it (or not), passes it to your app, and then takes the response, does something again (or not), and sends it back. Simple, right? It's super useful for tasks that need to happen globally, like adding headers or compressing data.
In FastAPI, you create middleware with the @app.middleware("http") decorator or use built-in options like GZipMiddleware. Let's jump into our examples.
First, let's add a middleware that tracks how long it takes to process a request. This is great for spotting slowdowns or debugging.
Here's how it works: when a request hits, we mark the start time. Then, we let the app handle it and get a response. Once that's done, we check the time again, calculate the difference, and add it as a header called X-Process-Time.
Here's the code:
import time from fastapi import FastAPI, Request app = FastAPI() @app.middleware("http") async def add_process_time_header(request: Request, call_next): start_time = time.perf_counter() response = await call_next(request) process_time = time.perf_counter() - start_time response.headers["X-Process-Time"] = str(process_time) return response
We use time.perf_counter() because it's more precise than time.time() for short measurements. One thing to note: if you want browsers to see this header, you might need to adjust your CORS settings with expose_headers. But that's optional and depends on your setup.
Next up, let's make your app safer with security headers. These tell browsers how to handle your site and can block common attacks like clickjacking or cross-site scripting.
Here's a middleware that adds them:
from fastapi import Request async def add_security_headers(request: Request, call_next): response = await call_next(request) response.headers["X-Frame-Options"] = "DENY" response.headers["X-Content-Type-Options"] = "nosniff" response.headers["Strict-Transport-Security"] = "max-age=63072000; includeSubDomains" response.headers["Permissions-Policy"] = ( "geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=()," "magnetometer=(),gyroscope=(),fullscreen=(self),payment=()" ) response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin" response.headers["Content-Security-Policy"] = ( "default-src 'self'; " "script-src 'self' 'unsafe-inline' data: https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://www.google.com https://www.gstatic.com https://z.clarity.ms https://*.clarity.ms https://*.googlesyndication.com; " "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://cdnjs.cloudflare.com; " "img-src 'self' data: https://lh3.googleusercontent.com https://*.twimg.com https://*.fbcdn.net https://*.ytimg.com https://*.fna.fbcdn.net https://*.clarity.ms https://*.tiktokcdn.com https://*.tiktokcdn-us.com image/; " "font-src 'self' data: https://fonts.gstatic.com; " "object-src 'none'; " "form-action 'self'; " "frame-src 'self' https://www.google.com https://www.gstatic.com; " "connect-src 'self' https://*.clarity.ms https://z.clarity.ms; " "upgrade-insecure-requests" ) return response
That's a lot, so let's break it down:
You might need to tweak these based on your app - say, if you use external scripts or images, adjust the CSP. But this is a solid base.
Finally, let's speed things up with compression. The GZipMiddleware compresses responses if the client says it's cool with GZip (via the Accept-Encoding: gzip header). Smaller responses mean faster load times.
Here's how to use it:
from fastapi import FastAPI from fastapi.middleware.gzip import GZipMiddleware app = FastAPI() app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=5)
The minimum_size=1000 means it only compresses responses over 1000 bytes - smaller ones aren't worth the effort. The compresslevel=5 is a middle ground: 1 is fast but less compression, 9 is slow but shrinks more. Five works well for most cases.
When a client asks for GZip, this middleware compresses the response body and adds Content-Encoding: gzip. It's a quick win for performance, especially on slow networks.
Now, how do we put these together? And does the order matter?
Yes, it does.
In FastAPI (and Starlette, which it's built on), middlewares run in the order you add them for requests and reverse order for responses. So, the first one added is the first to see the request and the last to touch the response.
Imagine this:
Here's how to set it up:
from fastapi import FastAPI from starlette.middleware.base import BaseHTTPMiddleware from fastapi.middleware.gzip import GZipMiddleware app = FastAPI() # Security headers first app.add_middleware(BaseHTTPMiddleware, dispatch=add_security_headers) # Then process time app.add_middleware(BaseHTTPMiddleware, dispatch=add_process_time_header) # GZip last app.add_middleware(GZipMiddleware, minimum_size=1000)
Notice we use BaseHTTPMiddleware for custom functions - it's a wrapper FastAPI needs. Built-in ones like GZipMiddleware go straight in.
This order works because security and process time only add headers, and GZip compresses the body. Headers aren't compressed, so adding them after GZip is fine. But if a middleware needed to read the response body, you'd put it before GZip to see the uncompressed version.
Think about what each middleware does and plan the sequence.
Middleware in FastAPI is a clean way to add features across your app. We've covered tracking request time, boosting security with headers, and speeding up responses with compression. And we've seen why order matters - get it right, and everything runs smoothly.
So, go add some middleware to your FastAPI app. It's worth it for the performance, safety, and efficiency you'll gain. Have fun building!
Enjoyed this blog post? Check out these related posts!
Adding middleware to FastAPI Applications: Process Time, Security, and Compression
A practical guide to implementing middleware in FastAPI for better performance, security, and efficiency.
Read More..
Simple CI/CD for Your FastAPI App with Google Cloud Build and Cloud Run
Push code, deploy automatically: A simple CI/CD guide for your web app.
Read More..
Optimizing Reflex Performance on Google Cloud Run
A Comparison of Gunicorn, Uvicorn, and Granian for Running Reflex Apps
Read More..
Reflex Makes SEO Easier: Automatic robots.txt and sitemap.xml Generation
Discover how adding your deploy URL in Reflex automatically generates robots.txt and sitemap.xml for easier SEO.
Read More..
Have a project in mind? Send me an email at hello@davidmuraya.com and let's bring your ideas to life. I am always available for exciting discussions.