<p>Access-Control-Allow-Origin: * 与 Access-Control-Allow-Credentials: true 不可共存,因CORS协议禁止二者同时出现;生产环境需显式配置AllowOrigins切片(含完整协议+域名),并确保AllowMethods包含"OPTIONS"、AllowHeaders涵盖实际请求头、前端设credentials: "include"。</p>

直接用 cors.Default() 会放行所有域名,但一旦前端带凭证(比如 Cookie 或 Authorization),浏览器立刻拒绝响应——这不是功能没生效,而是 CORS 协议本身禁止 Access-Control-Allow-Origin: * 和 Access-Control-Allow-Credentials: true 同时出现。
为什么 cors.New() 必须显式配置 AllowOrigins 列表
因为 AllowAllOrigins: true(默认值)等价于硬塞 *,而生产环境几乎总要支持凭证。此时必须改用 AllowOrigins 切片,且每个元素必须是完整协议+域名:
-
"http://localhost:3000"✅ 可用于本地开发 -
"https://admin.example.com"✅ 生产域名 -
"example.com"❌ 缺少协议,会被忽略 -
"*.example.com"❌ Gin 的 cors 不支持通配符子域(除非用AllowOriginFunc自定义逻辑)
OPTIONS 预检失败的三个常见原因
浏览器对非简单请求(如 Content-Type: application/json、PUT 方法、带 Authorization 头)会先发 OPTIONS 请求。失败往往不是 CORS 配置错,而是链路问题:
-
AllowMethods里没包含"OPTIONS"→ 预检被直接拦截,返回 404 或 405 - CORS 中间件位置太靠后(比如放在 JWT 鉴权之后)→
OPTIONS请求仍会触发鉴权逻辑,因无 token 而报错 - 前端发了带 body 的
OPTIONS(某些 iOS WebView 有此行为)→ Gin 的cors默认只处理空 body 预检,需确认客户端行为或加日志排查
AllowCredentials: true 时必须同步设置的三项
只要开了凭证支持,以下三者缺一不可,否则前端 JS 拿不到响应或请求被静默丢弃:
-
AllowHeaders必须显式列出前端实际发送的头,比如["Content-Type", "Authorization"];漏掉Authorization就会导致带 token 的请求被拦在预检阶段 -
ExposeHeaders才能让前端 JS 读取自定义响应头(如X-Request-ID、Set-Cookie),否则response.headers.get("X-Request-ID")返回null - 前端
fetch或axios必须设credentials: "include"(或withCredentials: true),否则浏览器根本不会发 cookie
最易被忽略的是:开发时用 http://localhost:3000 测试,上线后忘了把生产域名加进 AllowOrigins 切片;或者 AllowCredentials: true 开了,但 AllowHeaders 还沿用默认值,漏掉了前端实际携带的认证头字段。


















