cors.Default()在生产环境不可用,因其等价于AllowAllOrigins:true,固定返回Access-Control-Allow-Origin:*,而该通配符与withCredentials:true冲突,违反CORS协议安全限制。

直接用 cors.Default() 就会放行所有域名,但一旦前端带 withCredentials: true,浏览器立刻拒绝响应——这不是配置没生效,而是 CORS 协议本身禁止 Access-Control-Allow-Origin: * 和 Access-Control-Allow-Credentials: true 同时出现。
为什么 cors.Default() 在生产环境不可用
它内部等价于 AllowAllOrigins: true,响应头固定写死 Access-Control-Allow-Origin: *。而只要前端 fetch 设置了 credentials: 'include'(或 jQuery 的 xhrFields: { withCredentials: true }),浏览器就会因安全策略直接拦截响应,控制台报错 Response to preflight request doesn't pass access control check: 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'。
- 开发阶段 localhost 多端口(
http://localhost:3000→http://localhost:8080)算跨域,必须显式加入AllowOrigins -
AllowOrigins必须带协议和端口,写"localhost:3000"或"*.example.com"都无效 - 如果用 HTTPS 前端,后端
AllowOrigins也必须是"https://...",混用 http/https 会被拒绝
cors.New() 白名单配置的关键参数组合
真正可控的配置必须用 cors.New() + 显式 cors.Config{},且以下四点要同时满足:
-
AllowOrigins设为具体切片,如[]string{"http://localhost:3000", "https://app.example.com"} -
AllowCredentials: true—— 否则带 cookie 或 token 的请求拿不到响应体 -
AllowHeaders至少包含前端实际发送的头,比如用了Authorization就得写进去,否则预检失败 -
ExposeHeaders要列出前端 JS 需要读取的自定义响应头,如"X-Request-ID",否则response.headers.get("X-Request-ID")返回null
示例片段:
router.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:3000", "https://app.example.com"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Content-Type", "Authorization", "X-Requested-With"},
AllowCredentials: true,
ExposeHeaders: []string{"X-Request-ID", "Content-Length"},
MaxAge: 12 * time.Hour,
}))
OPTIONS 预检失败的三个典型表现和修复点
浏览器对非简单请求(如 PUT、Content-Type: application/json、带 Authorization 头)会先发 OPTIONS 请求。常见问题:
- 返回 404:说明路由没被 CORS 中间件覆盖,检查
router.Use(...)是否在所有router.GET/POST之前调用 - 返回 401/403:鉴权中间件(如 JWT)跑在 CORS 之前,导致预检请求被拦,把鉴权中间件移到 CORS 之后
- 响应头缺
Access-Control-Allow-Methods:确认AllowMethods切片里包含了"OPTIONS",否则预检直接被浏览器拒收
调试命令:curl -X OPTIONS -H "Origin: http://localhost:3000" -H "Access-Control-Request-Method: PUT" -I http://localhost:8080/api/user,看响应头是否完整。
AllowOriginFunc 适合动态白名单场景
当允许的域名无法静态枚举(比如 SaaS 多租户子域名:`tenant1.app.com`、`tenant2.app.com`),用 AllowOriginFunc 替代 AllowOrigins:
- 函数返回
true才放行,且优先级高于AllowOrigins,设了它AllowOrigins就被忽略 - 注意:不能做耗时操作(如查 DB),否则每个预检请求都卡住
- 正则匹配要谨慎,
strings.HasSuffix(origin, ".app.com")比regexp.MatchString更轻量
示例:
AllowOriginFunc: func(origin string) bool {
return strings.HasSuffix(origin, ".app.com") || origin == "http://localhost:3000"
},
通配符(如 "https://*.example.com")不被原生支持,必须靠 AllowOriginFunc 实现;AllowWildcard: true 是个误导性字段,实际未被 gin-contrib/cors 使用。











