fiber 默认 cors 中间件不支持动态白名单,因 rs/cors 的 allowedoriginsfunc 在初始化时绑定,无法实时查库或远程服务;需用 sync.map 等线程安全结构配合热更新机制,并在函数中严格校验 origin 字符串、处理 credentials 和预检头。

为什么 Fiber 默认 CORS 中间件不支持动态白名单
Fiber 的 cors.New() 中间件底层用的是 rs/cors,它只接受静态的 AllowedOrigins 切片或预设函数(如 AllowedOriginsFunc),但该函数在中间件初始化时就绑定好了,无法在每次请求中实时查数据库、读配置中心或调用远程服务。如果你直接传一个闭包去查 map,看似“动态”,实则 map 本身没更新机制——热更新白名单会失败。
用 AllowedOriginsFunc 实现运行时校验的关键点
必须把白名单数据源设计成可并发安全读取、支持热更新的结构,比如 sync.Map 或带读锁的 map[string]struct{},再包裹进 AllowedOriginsFunc。注意:该函数签名是 func(origin string) bool,返回 true 才放行。
- Origin 字符串必须完整匹配(含协议、host、端口),不能只截 host;
https://a.com:8080和https://a.com是两个不同源 - 空 Origin 或解析失败(如 malformed URL)应直接返回
false,避免 fallback 到* - 若允许 credentials(
credentials: true),函数里匹配成功后必须原样返回该origin字符串,不能返回"*",否则浏览器拒绝响应 - 建议加日志打点,记录被拒绝的 Origin,便于排查误配
Fiber v3 中如何安全热更新白名单数据
Fiber v3 不再默认共享全局状态,所以白名单 map 不能靠包级变量硬编码。推荐做法是:启动时从 etcd/Redis 加载初始值,再起一个 goroutine 定期拉取或监听变更事件,更新到线程安全的存储中。中间件里只做只读访问。
- 别在
AllowedOriginsFunc里做 HTTP 请求或 DB 查询——会拖慢每个请求,且无超时控制 - 不要用
map直接赋值更新(如allowedOrigins = newMap),会导致竞态;要用sync.Map.Store()或写锁保护 - 如果用
sync.Map,注意它的Load返回interface{},需类型断言;建议封装一层IsOriginAllowed(origin string) bool函数统一处理 - Fiber v3 的
fiber.Ctx.Locals不适合存白名单,它是请求级上下文,不是全局状态容器
实际代码里最容易漏掉的 header 配置
cors.New() 默认不开启 credentials 支持,也不自动回传 Access-Control-Allow-Headers。哪怕你白名单逻辑完全正确,前端带 Authorization 或 X-Request-ID 时仍会卡在 OPTIONS 预检失败。
- 必须显式设置
AllowCredentials: true,否则fetch(..., { credentials: "include" })直接报错 -
ExposedHeaders要列出前端 JS 实际需要读取的响应头(如X-Total-Count),否则response.headers.get("X-Total-Count")返回null -
MaxAge建议设为3600(1 小时),减少重复预检请求,但别设太大,否则白名单更新后客户端缓存太久 - 如果后端接口返回 401/403,确保这些状态码也在
AllowMethods覆盖范围内,否则浏览器可能静默吞掉错误响应
curl -H "Origin: https://evil.com" -I http://localhost:3000/api/test 验证非法源是否被干净拦截。











