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.

Test your FastAPI CORS live โ†’

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"
📚 HttpFixer Blog โ€” fix guides, explainers, and references โ†’