fastapi的corsmiddleware必须在路由注册前挂载,否则预检请求无法拦截;allow_origins=["*"]与allow_credentials=true不可共存,否则配置被静默忽略。

FastAPI的CORSMiddleware必须在路由注册前挂载
中间件没生效,八成是顺序错了——CORSMiddleware必须在@app.get、@app.post或app.include_router()之前调用。它依赖ASGI生命周期早期介入响应头写入,一旦路由已注册或其它中间件提前终止请求流,CORS头就根本加不上。
- 正确写法:
app.add_middleware(CORSMiddleware, ...)紧接app = FastAPI()之后 - 错误写法:把
add_middleware放在所有@app.装饰器之后,或塞进某个APIRouter实例里(CORSMiddleware只对主app生效) - 如果用了
Starlette原生MiddleWare列表初始化,确保CORSMiddleware排在靠前位置,不能被后续中间件覆盖
allow_origins=["*"]和allow_credentials=True不能共存
这是浏览器强制的安全限制:只要后端返回Access-Control-Allow-Credentials: true,Access-Control-Allow-Origin就不能是通配符*。FastAPI检测到两者同时启用时,会静默忽略整个CORS配置,预检请求直接失败,控制台只报“CORS preflight failed”,不提示原因。
- 开发阶段用精确源:
allow_origins=["http://localhost:3000", "http://127.0.0.1:5173"] - 生产环境必须显式列出域名:
allow_origins=["https://myapp.com", "https://admin.myapp.com"] - 若需动态匹配,用
allow_origin_regex配合正则,如r"^https://.*\.myapp\.com$"
预检OPTIONS请求返回405或空响应?检查路由是否存在
CORSMiddleware能自动处理预检,但前提是请求路径真实存在。如果前端访问/api/users,而你只定义了/users,Uvicorn会先返回404,中间件根本没机会运行。
- 用
curl -X OPTIONS -H "Origin: http://localhost:3000" -I http://localhost:8000/api/users验证路径是否可达 - 确保
allow_methods显式包含"OPTIONS"(默认值不包含,必须写全:["GET", "POST", "PUT", "DELETE", "OPTIONS"]) - 若用了反向代理(Nginx),确认它透传了
Origin和Access-Control-Request-*头,否则预检被拦在网关层
异步环境下CORS头没暴露给前端?检查expose_headers
自定义响应头(比如X-Request-ID、X-RateLimit-Remaining)默认不会被浏览器读取。即使中间件加了头,前端JS仍拿不到,除非明确告诉CORS允许暴露。
- 在
app.add_middleware()中加上expose_headers=["X-Request-ID", "X-RateLimit-Remaining"] - 注意:
expose_headers只控制浏览器能否读取,不影响后端逻辑;不加的话,response.headers["X-Request-ID"] = "abc"虽生效,但fetch().headers.get("X-Request-ID")返回null - 常见漏项:
Content-Range(分页场景)、Retry-After(限流重试)
proxy配置把请求转发过去——既干净又避免多层CORS叠加出错。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











