Fix CORS in FastAPI
Last updated: April 2026
Browser Console Error
Access to fetch at 'http://localhost:8000/api' from origin 'http://localhost:5173'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
on the requested resource.
FastAPI uses Starlette's CORSMiddleware. Add it before any routes โ the middleware must be the first layer to handle every request including preflight OPTIONS.
Basic fix โ add CORSMiddleware
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://yourapp.com"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Development + production origins (Vite on localhost:5173)
Vite's dev server runs on port 5173 by default. Add it explicitly alongside your production domain:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"http://localhost:5173", # Vite dev server
"http://localhost:3000", # Create React App
"https://yourapp.com", # production
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
allow_origins=['*'] with allow_credentials=True raises ValueError
Server Startup Error
ValueError: allow_credentials=True is not allowed when allow_origins=['*'].
Use explicit origins instead.
The CORS spec forbids wildcard origins when credentials are involved. Starlette raises this at startup. Fix โ replace ['*'] with explicit origins:
# Breaks when frontend sends cookies or Authorization headers
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True, # raises ValueError
)
# Fix โ list origins explicitly
app.add_middleware(
CORSMiddleware,
allow_origins=["https://yourapp.com", "http://localhost:5173"],
allow_credentials=True,
)
# Also fine โ wildcard without credentials
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
# allow_credentials omitted โ defaults to False
)
โ Bearer tokens in an Authorization header do not require
allow_credentials=True. Only set it to True if your frontend sends cookies or uses credentials: 'include' in fetch.Dynamic origins with allow_origin_regex
For multiple subdomains or dynamic origins, use allow_origin_regex instead of allow_origins:
app.add_middleware(
CORSMiddleware,
allow_origin_regex=r"https://(www\.)?yourapp\.com",
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Vite proxy โ avoid CORS in development entirely
Configure Vite to proxy API requests to your FastAPI server. Your React app calls /api/endpoint โ Vite forwards it to FastAPI, same origin, no CORS needed in development:
// vite.config.ts
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
}
}
}
}
Starlette and raw ASGI apps
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
middleware = [
Middleware(
CORSMiddleware,
allow_origins=["https://yourapp.com"],
allow_methods=["*"],
allow_headers=["*"],
)
]
app = Starlette(routes=routes, middleware=middleware)
Verify your CORS headers
# Test preflight from terminal curl -X OPTIONS http://localhost:8000/api/endpoint \ -H "Origin: http://localhost:5173" \ -H "Access-Control-Request-Method: POST" \ -v 2>&1 | grep -i "access-control"