cors.default()在生产环境出问题,因其默认设access-control-allow-origin: 且未配allowcredentials,而浏览器禁止与credentials共存;预检失败导致静默请求拦截,须改用cors.new()明确配置alloworigins、allowcredentials等字段。

为什么 cors.Default() 在生产环境会出问题
它直接返回 Access-Control-Allow-Origin: *,但一旦你设置了 AllowCredentials: true(比如要传 Cookie 或 Authorization header),浏览器就会拒绝这个响应——因为 * 和 credentials 不能共存。这不是 Gin 的 bug,是浏览器的硬性限制。
常见现象:前端发请求时状态码是 200,但 response body 为空、response.headers 里看不到数据,控制台也没报错,就是“静默失败”。根本原因是预检(OPTIONS)没过,后续请求被浏览器拦截了。
- 开发阶段用
cors.Default()没问题,能快速跑通 - 只要涉及登录态(如
withCredentials: true),就必须指定明确的AllowOrigins或用AllowOriginFunc动态校验 -
cors.Default()默认不设MaxAge,预检每次都要重发,影响性能
cors.New() 配置里最关键的三个字段
别堆参数,只改这三个就能覆盖绝大多数真实场景:
-
AllowOrigins:填具体域名数组,例如[]string{"http://localhost:3000", "https://app.example.com"};不要写"*" -
AllowCredentials:必须和AllowOrigins配套设为true,否则前端 fetch 里设了credentials: 'include'就失效 -
AllowMethods和AllowHeaders:按需精简,别无脑全写;常见组合是[]string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}和[]string{"Content-Type", "Authorization", "X-Requested-With"}
示例片段:
router.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:3000", "https://prod.example.com"},
AllowCredentials: true,
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Content-Type", "Authorization"},
MaxAge: 12 * time.Hour,
}))
OPTIONS 请求返回 204 而不是 200 的原因
预检请求(OPTIONS)成功后,规范要求返回 204 No Content,不是 200 OK。虽然部分旧版浏览器容忍 200,但 Chrome、Safari 等现代浏览器会严格校验——返回 200 会导致预检通过但后续请求被丢弃。
gin-contrib/cors 内部已正确处理这点,只要你不用自己手写的中间件覆盖它,就不用额外干预。但如果看到 OPTIONS 返回 200,说明你可能:
- 在路由里写了
router.OPTIONS(...)手动处理,且用了c.AbortWithStatus(200) - 或在其他中间件里提前写了响应头/状态码,干扰了 cors 中间件执行
- 或用了过时的自定义中间件(比如从 2021 年博客抄的代码)
动态 Origin 校验:用 AllowOriginFunc 替代白名单
当你的前端有多个子域名(如 zh.example.com、en.example.com),或者要对接第三方嵌入场景,硬编码 AllowOrigins 不现实。这时用 AllowOriginFunc 更安全灵活:
AllowOriginFunc: func(origin string) bool {
return strings.HasSuffix(origin, ".example.com") && !strings.Contains(origin, "evil.com")
},
注意两点:
- 这个函数优先级高于
AllowOrigins,设了它,AllowOrigins就失效 - 函数里别做耗时操作(如 DB 查询、HTTP 调用),否则每个预检请求都会卡住
- 务必校验 origin 是否为空字符串(某些爬虫或异常请求可能不带 Origin 头)
复杂点在于:Origin 头可被伪造,所以它只是第一道过滤,不能替代后端鉴权逻辑。











