CORS 带凭据与通配符互斥 (CORS Credentials vs Wildcard)
1. 一句话
浏览器规定:响应带 Access-Control-Allow-Credentials: true 时,Access-Control-Allow-Origin / Allow-Headers / Allow-Methods 都不许是 *。想让"带 Cookie 的跨域请求"通过,服务器必须回显请求方的具体来源和请求头,而不是发通配符。
2. 发生条件
- 前端要跨域发请求,并且带凭据(Cookie /
Authorization+withCredentials)。 - 后端 CORS 中间件图省事配了"允许所有"(
*)。
3. 症状 / 报错
分两层,容易认错:
| 层 | 表现 |
|---|---|
| 浏览器 | The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include';预检(OPTIONS)过不去,正式请求根本没发出 |
| 框架 | tower-http 会在构造时直接 panic:Invalid CORS configuration: Cannot combine 'Access-Control-Allow-Credentials: true' with 'Access-Control-Allow-Headers: *' —— 服务起不来,跟浏览器无关 |
第二层特别坑:本地 curl 完全正常,测试也过不了,因为进程在 Router 构造阶段就炸了。
4. 试过但没用的解法
- 把
Allow-Headers从*改成列一堆头 → 能跑,但每加一个自定义头(如X-Trace-Id)就得回来改一次,漏了就静默失败。 - 干脆关掉
allow_credentials(true)→ 浏览器不再带 Cookie,需要登录态的功能全废。
5. 根因
* 的语义是"任何人",credentials: true 的语义是"允许携带用户身份"。两者叠加等于对任意站点开放用户凭据,浏览器直接禁止。规范如此,不是框架的锅。
正确做法是动态回显:把请求里的 Origin、Access-Control-Request-Headers、Access-Control-Request-Method 原样回写。回显在效果上等价于"允许所有",但范围被限制在对方自己声明要用的东西上,不构成对任意站的开放。
6. 最终解法
tower-http(Rust / axum)
use tower_http::cors::{AllowHeaders, AllowMethods, AllowOrigin, CorsLayer};
CorsLayer::new()
.allow_origin(AllowOrigin::list(origins)) // 白名单,逐个 parse 成 HeaderValue
.allow_credentials(true)
.allow_methods(AllowMethods::mirror_request()) // 回显 Access-Control-Request-Method
.allow_headers(AllowHeaders::mirror_request()) // 回显 Access-Control-Request-Headers注意 mirror_request() 只在预检请求里回显;普通请求不带这两个头,也就自然切回手头默认值,不会出错。
Starlette / FastAPI
框架已经内建了这套回显逻辑:allow_methods=["*"] + allow_headers=["*"] + allow_credentials=True 在 Starlette 里是合法的,它在预检时自动把 Access-Control-Request-Headers 回显出去。所以从 FastAPI 迁到 axum 时,不能照抄"配 * 就行"的写法。
7. 下次怎么识别
- 报错里同时出现
Access-Control-Allow-Credentials和*(或cannot combine)→ 直接看第 6 节。 - 从 Python/Starlette 迁到 Rust/axum:CORS 配置要重写,不要照抄,两者的"全允许"语义不同。
- 反向代理也要同步检查:nginx 里手写
add_header Access-Control-Allow-Origin "*"的,遇到带凭据请求同样会被浏览器拦。
8. 相关概念
- CSP (Content Security Policy 内容安全策略) —— 同为"浏览器侧安全策略,服务器下发布置"。
- Web 登录鉴权 (Session, Cookie, Bearer, JWT) ——
credentials: true服务的对象就是这里的 Cookie / Bearer。
来源:2026-09 迁移 RoleFit 管理后台(FastAPI → axum)时,tower-http 在 Router 构造阶段 panic 暴露此差异。本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Web