CORS and Middleware in FastAPI
Learn how to configure CORS and middleware in FastAPI step by step. This lesson covers the core concepts, hands-on setup, troubleshooting, and what to explore next in the FastAPI Backend Development track.
Focus: adding cors and middleware configuration
Your FastAPI API is working locally, but the moment you point a React or Vue frontend to it, the browser slams the brakes. A dark red CORS error appears in the console, and your requests silently fail — even though your endpoints are perfectly fine. This lesson solves that pain and the related headache of global request handling by teaching you how to add CORS and middleware configuration in FastAPI, step by step, so you can connect frontends, log requests, compress responses, and even create your own middleware hooks.
The problem this lesson solves
Imagine finishing a beautiful FastAPI backend and then wiring up a SPA running on http://localhost:3000. Your API is on http://localhost:8000. You try to fetch data, but the browser refuses to show it. That’s Cross-Origin Resource Sharing (CORS) throwing a security fit. Browsers enforce the same-origin policy by default, blocking requests from a different origin — meaning different scheme, host, or port. Without explicit permission from your server, any frontend on another port is out of luck.
But CORS isn’t the only hidden issue. You also need to handle tasks that happen for every request — logging, timing, gzip compression, custom headers — and manually adding code to every endpoint is a maintenance nightmare. That’s where middleware enters the picture. This lesson shows you how to fix browser CORS errors and add reusable request/response processing in FastAPI, so your API is both open to legitimate clients and cleanly instrumented.
Core concept / mental model
Think of your FastAPI app as a building with one main entrance. Middleware is the security checkpoint and reception desk just inside that entrance. Every request — no matter which endpoint it’s headed to — must pass through this checkpoint first. The middleware can log it, modify it, reject it, or let it through. After the endpoint runs, the response comes back through the same checkpoint for optional post-processing.
CORS is a specific kind of HTTP header negotiation, not a separate server. When a browser makes a cross-origin request, it first sends a preflight OPTIONS request asking, "Hey, am I allowed to talk to this server?" The server must reply with headers like Access-Control-Allow-Origin. FastAPI implements CORS as a middleware that automatically adds these headers and handles preflight requests for you.
Pro tip — Visualize the request path: Browser → CORS middleware → your custom middleware (e.g., logging) → endpoint → response → back through middleware → browser. Middleware order matters because each layer wraps the next.
How it works step by step
-
Install FastAPI with the standard extras (if you haven’t already):
bash pip install "fastapi[all]"Theallextra includesuvicornfor running the server andpython-multipartfor forms, which you might need later. -
Import the CORS middleware in your main app file:
python from fastapi.middleware.cors import CORSMiddleware -
Define a list of allowed origins — the frontend URLs you trust, e.g.,
["http://localhost:3000"]. -
Add the middleware to your app instance using
.add_middleware():python app.add_middleware( CORSMiddleware, allow_origins=allowed_origins, allow_credentials=True, allow_methods=["*"], # all HTTP methods allow_headers=["*"], # all request headers ) -
Understand the parameters: -
allow_origins: list of exact origins (with scheme and port). -allow_credentials: setTrueif you send cookies or auth headers. -allow_methods:"*"allows all, or restrict to["GET", "POST"]. -allow_headers:"*"allows all, or list specific headers like"Authorization". -
Create custom middleware using either a
@app.middleware("http")decorator or a fullBaseHTTPMiddlewareclass. The decorator version is cleaner for simple tasks like logging. -
Test with a real frontend or with
curlto verify the headers are present (see hands-on below).
Hands-on walkthrough
Let’s build a minimal FastAPI app that enables CORS and adds a request‑logging middleware. Create a file named main.py:
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
import time
app = FastAPI()
# 1. CORS configuration
allowed_origins = [
"http://localhost:3000", # React dev server
"http://127.0.0.1:3000", # alternative localhost
]
app.add_middleware(
CORSMiddleware,
allow_origins=allowed_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 2. Custom middleware to log request duration
@app.middleware("http")
async def log_requests(request: Request, call_next):
start = time.perf_counter()
response = await call_next(request) # pass request to the next layer
duration = time.perf_counter() - start
print(f"{request.method} {request.url.path} took {duration:.4f}s")
return response
@app.get("/")
async def root():
return {"message": "Hello World"}
@app.get("/data")
async def get_data():
return {"items": [1, 2, 3]}
Run the server:
uvicorn main:app --reload --port 8000
Now test the CORS headers with curl:
curl -i http://localhost:8000/data
You’ll see output like:
HTTP/1.1 200 OK
access-control-allow-origin: http://localhost:3000
access-control-allow-credentials: true
content-type: application/json
...
Pro tip — The
access-control-allow-originheader appears even when the request is not from a browser, because FastAPI adds it unconditionally. That’s fine — the browser only enforces it when needed.
Now send a preflight request to simulate what a browser does:
curl -i -X OPTIONS http://localhost:8000/data \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: GET"
You should get a 200 OK with access-control-allow-* headers — CORS is properly configured.
Compare options / when to choose what
CORS comes in different flavors depending on your needs. Here’s a quick comparison:
| Approach | Use case | Pros | Cons |
|---|---|---|---|
allow_origins=["*"] |
Public APIs with no auth | Simplest | Cannot combine with allow_credentials=True |
| Specific origins list | Internal frontends or known domains | Secure, works with auth | Must update when deploying to new domains |
| Environment‑based origins | Development vs. production | No code changes for different envs | Requires extra config management |
BaseHTTPMiddleware |
Reusable, complex middleware logic | Class‑based, state possible | Slightly more boilerplate |
@app.middleware("http") |
Simple logging or header injection | Minimal, inline | Harder to test in isolation |
When to choose what:
- For a dev frontend on localhost, hardcode a specific origin list. Never use "*" with credentials.
- For production, load the allowed origins from an environment variable or a config file.
- For custom middleware, start with the decorator for trivial tasks. If you need to manage state or want to reuse the middleware across projects, use a BaseHTTPMiddleware subclass.
Troubleshooting & edge cases
- CORS error persists — Check that the
Originheader in the request exactly matches an entry inallow_origins.http://localhost:3000andhttp://127.0.0.1:3000are different origins. allow_origins=[""]doesn’t work with cookies — Whenallow_credentials=True, FastAPI requires a specific origin. Replace""with the exact frontend origin.- Preflight returns 404 — Ensure your route is defined and that you didn’t accidentally replace FastAPI’s default
OPTIONShandling. If you mounted a sub‑app, CORS middleware might be applied to the wrong app. - Middleware not executed — Middleware added after the app has started may not be picked up. Always call
add_middlewarebefore running the app. - Custom middleware blocks requests — If your middleware raises an exception before calling
call_next, the endpoint never runs. Wrap the call in try/except to handle errors gracefully. - Double middleware logging — You might see duplicate logs if the same middleware is registered twice or if you have both a decorator and manual
add_middleware. Check your imports and registration order.
What you learned & what's next
You now understand the core idea behind adding CORS and middleware configuration: you can control cross-origin access and intercept every request/response globally. You completed a practical exercise that sets up CORS headers, handles preflight requests, and adds a logging middleware. You also learned how to troubleshoot common pitfalls like origin mismatch and wildcards with credentials.
Next in the FastAPI Backend Development track, you’ll explore dependencies and dependency injection — a powerful way to share authentication, database sessions, and validation logic across many endpoints without repeating code. With your API now open to the frontend and covered by middleware, you’re ready to build more advanced features like user authentication and request-scoped resources.
Key takeaways from this lesson:
- CORS is a browser security mechanism; FastAPI handles it via the CORSMiddleware.
- Middleware runs for every request/response, making it ideal for logging, compression, and header injection.
- Use specific allow_origins for production, never "*" when allow_credentials=True.
- The @app.middleware("http") decorator is great for simple tasks; BaseHTTPMiddleware for complex, reusable logic.
- Always register middleware before the app starts and test preflight requests with curl.
- Middleware order matters — each layer wraps the next, so think about the execution sequence.
Practice recap
Create a new FastAPI app and add both CORS middleware (with two origins) and a custom middleware that adds a custom X-Process-Time header to every response, measuring the processing time. Then run uvicorn and use curl -i to verify both the CORS headers and your custom header appear. Test preflight with an OPTIONS request to confirm it returns 200.
Common mistakes
- Using
allow_origins=["*"]together withallow_credentials=True— FastAPI will throw an error because wildcard origins can't work with credentials. Use exact origins instead. - Forgetting to include the scheme and port in origins —
localhost:3000is not valid; you needhttp://localhost:3000. - Adding middleware after the app has already started (e.g., in a test or after the first request) — the middleware may not be applied, leading to confusion.
- Assuming CORS errors are a server problem when the browser is caching a failed preflight response — clear browser cache or hard‑reload before debugging.
- Restricting
allow_methodsto a list that doesn't includeOPTIONS— though FastAPI adds it automatically, some reverse proxies may block it if not allowed.
Variations
- Use
BaseHTTPMiddlewareto create a reusable class-based middleware with custom initialization (e.g., for logging services or rate limiting). - Load allowed origins from environment variables or a settings file using Pydantic settings, so you don't hard-code domains for different environments.
- Employ a third-party middleware like
gzip-middlewarefor compression, orTrustedHostMiddlewarefrom Starlette to validate the Host header against a fixed list.
Real-world use cases
- Enabling a React or Vue frontend on a different port to call your FastAPI backend without CORS errors in development and production.
- Adding structured request logging middleware to capture metrics (e.g., response time, status codes) for every API call in a microservices dashboard.
- Implementing a security middleware that rejects requests from unauthorised origins or adds custom security headers (e.g., CSP) before the endpoint runs.
Key takeaways
- CORS errors are browser-enforced; FastAPI uses
CORSMiddlewareto add the correct headers and handle preflight OPTIONS requests. - Middleware in FastAPI wraps every request/response, making it perfect for logging, compression, and custom header injection.
- Always set
allow_originsto the exact frontend origin list — never"*"whenallow_credentials=True. - Order matters: middleware added earlier wraps later ones, so think about the execution sequence when combining CORS and custom middleware.
- Use the
@app.middleware("http")decorator for simple tasks, andBaseHTTPMiddlewarefor reusable, stateful logic. - Testing with
curlwith anOriginheader lets you verify CORS headers without needing a full browser session.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.