生产环境必须用动态白名单校验Origin,不能硬编码["*"]或静态列表——前者在AllowCredentials:true时浏览器直接拒收,后者无法应对灰度、多租户或域名频繁变更;AllowOriginFunc支持查配置中心、DB或Redis,运行时热更新且优先级高于AllowOrigins,需做空Origin、协议校验并确保返回false时正确fallback,同时必须透传Vary: Origin头防缓存污染。

生产环境必须用动态白名单校验 Origin,不能硬编码 []string{"*"} 或静态列表——前者在 AllowCredentials: true 时直接失效,后者无法应对灰度、多租户或域名频繁变更场景。
为什么 AllowedOriginsFunc 比 AllowedOrigins 更适合微服务
微服务常需按路由路径、租户 ID 或请求头动态放行 Origin,而 gorilla/handlers.AllowedOrigins([]string{...}) 和 gin-contrib/cors.Config.AllowOrigins 都只支持静态匹配。一旦前端部署多个子域(如 app-v1.example.com、app-v2.example.com),或需对接第三方 SaaS 前端,静态列表就不可维护。
-
AllowedOriginsFunc允许你写任意逻辑:查配置中心、数据库、Redis 缓存,甚至调用内部鉴权服务 - 它在每次请求时执行,天然支持运行时热更新白名单,无需重启服务
- 注意返回
false时,gorilla/handlers会跳过设置Access-Control-Allow-Origin头,浏览器直接拒绝;gin-contrib/cors则默认 fallback 到不设该头(等效于拒绝) - 别忘了在函数里做基础校验:空
Origin、非法协议(data:、file:)、末尾斜杠不一致等问题
gin-contrib/cors 中 AllowOriginFunc 的正确用法
gin-contrib/cors 的 AllowOriginFunc 优先级高于 AllowOrigins,且会在 OPTIONS 和实际请求中都调用——这是它比手写中间件更可靠的关键点。
- 必须显式传入
cors.Config{AllowOriginFunc: func(origin string) bool { ... }},不能混用AllowOrigins和AllowOriginFunc - 函数内不要做耗时操作(如同步 HTTP 调用),否则阻塞整个请求链;建议加超时和缓存
- 示例逻辑(含缓存与协议校验):
func isOriginAllowed(origin string) bool { if origin == "" { return false } u, err := url.Parse(origin) if err != nil || u.Scheme != "https" && u.Scheme != "http" { return false } // 假设白名单已从 etcd 加载到内存 map[string]struct{} _, ok := allowedOriginsMap[origin] return ok } - 若用 Kratos Gateway,其内置 CORS 中间件也支持回调,但底层是
rs/cors,需显式设AllowedOriginsFunc
gorilla/handlers.CORS 动态白名单的坑与绕过
gorilla/handlers.CORS 不提供 AllowOriginFunc,但可通过 handlers.AllowedOriginsFunc 替代——注意不是同名方法,拼写是 AllowedOriginsFunc(带 d)。
- 它接收一个
func(origin string) bool,返回true时才设置Access-Control-Allow-Origin头 - 常见错误:漏掉
handlers.ExposedHeaders或handlers.MaxAge,导致前端拿不到自定义响应头或预检反复触发 - 若你用
gorilla/mux,确保 CORS 中间件包裹的是r(router),而不是某个 handler;否则/api/user可能绕过 - 子域名通配(如
*.example.com)不被标准支持,必须手动解析origin并用strings.HasSuffix或正则校验
动态白名单必须补全 Vary: Origin 头
无论用哪个库,只要 Origin 是动态校验的,就必须让代理、CDN 或浏览器知道这个响应依赖于 Origin 请求头——否则缓存可能污染:同一份 Access-Control-Allow-Origin: https://a.com 响应被错误地返回给 https://b.com 的请求。
-
gorilla/handlers和rs/cors默认加Vary: Origin,但手写中间件或某些封装层(如旧版 Kratos)可能遗漏 - 检查响应头:用
curl -I -H "Origin: https://a.com" http://localhost:8080/api,确认返回中含Vary: Origin - 如果用了 Nginx 做前置代理,需确保它透传并缓存
Vary头,配置项为proxy_cache_key $scheme$host$request_uri$sent_http_vary; - 漏掉
Vary: Origin是线上最隐蔽的 CORS 故障之一:现象是部分用户偶尔失败,复现困难,日志里还全是 200


















