Starlette CORSMiddleware: allow_origins=['*'] with allow_credentials=True
Wildcards cannot be used with credentials — specify explicit origins instead.
Replace allow_origins=["*"] with an explicit list: allow_origins=["https://yourdomain.com"]. Wildcards cannot be used with credentials — this is a browser security requirement, not a Starlette bug.
Why this error occurs
The CORS spec forbids Access-Control-Allow-Origin: * when a request includes credentials (cookies, Authorization headers, or TLS client certificates). Starlette enforces this at the middleware level — it raises a ValueError at startup rather than sending an invalid response that browsers would reject anyway.
This affects both FastAPI (which uses Starlette's CORSMiddleware internally) and standalone Starlette apps.
Fix 1 — Explicit origin list (recommended)
Replace the wildcard with the exact origins your frontend uses.
FastAPI
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://app.yourdomain.com", "https://yourdomain.com"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Starlette standalone
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
middleware = [
Middleware(
CORSMiddleware,
allow_origins=["https://app.yourdomain.com"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
]
app = Starlette(middleware=middleware)
Fix 2 — allow_origin_regex for dynamic origins
If you need to allow multiple subdomains or dynamic origins, use allow_origin_regex instead of allow_origins. It accepts a single regex pattern and works with allow_credentials=True.
app.add_middleware(
CORSMiddleware,
allow_origin_regex=r"https://(www\.)?yourdomain\.com",
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
For multiple domains:
# Matches yourdomain.com, app.yourdomain.com, anotherdomain.com
allow_origin_regex=r"https://(.+\.)?yourdomain\.com|https://anotherdomain\.com"
Fix 3 — Dynamic origin validation (advanced)
For full control — validate origins against a database or config at request time:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
ALLOWED_ORIGINS = {"https://app.yourdomain.com", "https://yourdomain.com"}
app = FastAPI()
@app.middleware("http")
async def cors_middleware(request: Request, call_next):
origin = request.headers.get("origin", "")
response = await call_next(request)
if origin in ALLOWED_ORIGINS:
response.headers["Access-Control-Allow-Origin"] = origin
response.headers["Access-Control-Allow-Credentials"] = "true"
response.headers["Vary"] = "Origin"
return response
Vary: Origin when the Access-Control-Allow-Origin value changes per request. Without it, CDNs and proxies may cache the wrong origin value and serve it to other users.
What does allow_credentials=True actually do?
It adds Access-Control-Allow-Credentials: true to every response. This tells the browser it may include cookies, Authorization headers, and TLS client certificates in cross-origin requests. The browser will then reject any response that has Access-Control-Allow-Origin: * alongside this header — which is why Starlette prevents the combination at startup.
Do I actually need allow_credentials=True?
Only if your frontend explicitly sets credentials: "include" in fetch calls, or withCredentials: true in XMLHttpRequest. If you are just sending JSON with an Authorization header (Bearer token), you do not need allow_credentials=True — Bearer tokens are not credentials in the CORS sense. You can use allow_origins=["*"] safely with Bearer token auth.
# Bearer token auth — credentials=True NOT needed
fetch("/api/data", {
headers: { "Authorization": "Bearer " + token }
})
# Cookie auth — credentials=True IS needed
fetch("/api/data", { credentials: "include" })
Starlette version history
This validation has been present since Starlette 0.14. The error message changed slightly in 0.20 — older versions may show a different message but the fix is identical. As of 2026 the current stable version is Starlette 0.46.x.
Test your CORS config live with CORSFixer →