gin-contrib/cors中allowallorigins:true与allowcredentials:true互斥,必须显式指定alloworigins如["http://localhost:3000"],且options请求须返回204并调用c.abort()终止链。

直接用 gin-contrib/cors 但别开 AllowAllOrigins: true
开发阶段图省事开 AllowAllOrigins: true 看似能跑通,但只要前端带 credentials: true(比如发请求时带 Cookie 或 Authorization header),浏览器就会直接拒绝——因为 Access-Control-Allow-Origin: "*" 和 Access-Control-Allow-Credentials: "true" 是互斥的。
正确做法是显式列出可信源:
AllowOrigins: []string{"http://localhost:3000", "https://prod.example.com"}- 如果需要动态匹配(比如多子域名),用
AllowOriginFunc替代AllowOrigins - 生产环境绝对不要留
*,哪怕只配一个临时测试域名也比通配安全
OPTIONS 请求没响应?检查中间件是否提前 c.Abort()
手动写 CORS 中间件时,常见错误是:收到 OPTIONS 请求后只设 header 就返回,没调 c.Abort()。结果 Gin 继续往下走,可能被后续中间件或路由处理逻辑干扰,甚至返回 404 或空响应体。
必须明确终止链:
- 判断
c.Request.Method == "OPTIONS" - 设置所有必要 header:
Access-Control-Allow-Methods、Access-Control-Allow-Headers等 - 调用
c.AbortWithStatus(204)—— 注意是204,不是200;204表示“成功但无响应体”,符合 RFC 规范
AllowCredentials 开了,但前端仍拿不到 Cookie
光在服务端设 Access-Control-Allow-Credentials: "true" 不够,前端 fetch 或 axios 也要同步配 credentials: "include"(fetch)或 withCredentials: true(axios)。漏掉任一端,Cookie 都不会自动带上。
另外注意:
- 一旦开了
AllowCredentials,AllowOrigins就不能是*,否则浏览器静默失败 - 如果前端用的是
localhost:3000,服务端AllowOrigins必须精确匹配协议+域名+端口,少一个都算跨域 - Chrome 对 localhost 的 CORS 检查更严格,建议开发时统一用
http://127.0.0.1:3000避免协议/域名歧义
预检缓存 MaxAge 设太高反而容易出问题
MaxAge 控制浏览器缓存预检响应的时间(单位秒),设成 86400(24 小时)看似减少 OPTIONS 请求,但实际带来两个麻烦:
- 改完 CORS 配置后,前端要等缓存过期才能生效,调试成本变高
- 某些旧版 Safari 对大数值
MaxAge处理异常,导致预检失败 - 建议开发期设
300(5 分钟),上线后再按需调高
真正容易被忽略的是:MaxAge 只影响 OPTIONS 请求,不影响实际接口响应;它不解决跨域逻辑本身,只是优化网络往返。











