fiber配置cors携带cookie时,必须设allowcredentials:true且alloworigins不能为"*",否则浏览器静默拒绝响应;应使用alloworiginfunc动态校验并原样返回合法origin,同时显式配置allowheaders、exposeheaders和maxage。

直接说结论:Fiber 的 CORS 必须用 cors.New() 中间件,硬编码白名单不安全,动态校验必须用 AllowOriginFunc,且 AllowCredentials: true 时绝不能返回 "*"。
为什么 AllowOrigins: []string{"*"} 在带 Cookie 场景下一定失败
浏览器明确禁止:只要响应头里有 Access-Control-Allow-Credentials: true,Access-Control-Allow-Origin 就不能是 "*"。否则 fetch 会静默拒绝响应,控制台只报 “CORS error”,不提示具体原因。
常见错误现象:
- 前端
fetch("/api/user", { credentials: "include" })返回空响应,Network 面板显示 status 200 但 response body 为空 - Chrome 控制台出现
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'
正确做法:
- 必须显式设置
AllowCredentials: true - 白名单不能用
"*",得用AllowOriginFunc动态返回原始origin字符串(如"https://myapp.com") - 函数里对空
origin、非法格式(如"null"或无协议字符串)一律返回false
AllowOriginFunc 怎么写才安全且支持热更新
Fiber v3 的 cors.New() 底层依赖 rs/cors,它的 AllowOriginFunc 是初始化时绑定的闭包,内部变量不会自动刷新。直接塞一个查 map 的闭包,map 变了中间件也感知不到。
关键点:
- 白名单数据源必须线程安全:用
sync.Map或读写锁保护的map[string]struct{} - 不要在
AllowOriginFunc里做 DB 查询或 HTTP 调用——每个请求都走一次,性能崩盘且无超时 - 匹配逻辑要严格:用
strings.HasSuffix(origin, ".mycompany.com"),别用strings.Contains(origin, "mycompany")(防evilmycompany.com) - 匹配成功后,必须原样返回
origin,不是返回true就完事;返回"*"或其他字符串会导致凭证被拒 - 建议加日志:
log.Printf("CORS rejected origin: %s", origin),方便排查前端发错 Origin
容易漏掉的 header 和预检失败原因
即使 Origin 校验通过,前端带 Authorization 或自定义头(如 X-Trace-ID)时,仍会在 OPTIONS 预检阶段卡住,因为浏览器要确认服务端是否允许这些头。
必须显式配置:
-
AllowHeaders: []string{"Content-Type", "Authorization", "X-Request-ID"}—— 列出所有前端可能带的非简单头 -
ExposeHeaders: []string{"X-Total-Count", "X-Rate-Limit-Remaining"}—— 前端 JS 要读的响应头必须在这里声明 -
MaxAge: 3600—— 缓存预检结果,减少 OPTIONS 请求频次
注意:cors.New() 默认不开启任何 header 白名单,也不暴露任何额外响应头,全靠手动填。
中间件注册顺序影响 CORS 生效范围
CORS 中间件的位置决定它能覆盖哪些路由。Fiber 没有“全局优先级”,只有执行顺序。
典型陷阱:
- 把
app.Use(cors.New(...))放在app.Use(jwt.New(...))后面 → JWT 鉴权失败的 401 响应,CORS 头没加上,前端拿不到错误详情 - 用
app.Group("/api", auth)但忘了给这个 group 加 CORS →/api下所有路由都不带 CORS 头 - 想对健康检查接口
/health关闭 CORS,却只给/api加了中间件 →/health仍被全局 CORS 覆盖(因app.Use()作用于全部路径)
推荐写法:app.Use(logger.New()) → app.Use(recover.New()) → app.Use(cors.New(...)) → 其他业务中间件。这样 panic 日志、CORS 响应头、鉴权流程全链路可控。
最常被忽略的是:动态白名单函数里没做 Origin 完整性校验(比如没检查协议+host+端口),或热更新机制没配锁导致竞态。上线前务必用 curl -H "Origin: https://evil.com" -I http://localhost:3000/api/test 测拒否。











