因为当allowcredentials为true时,access-control-allow-origin不能设为"*",否则浏览器直接拒收响应;必须显式匹配origin值,且中间件需在路由注册前调用、对options返回204并终止链。

为什么直接写 c.Writer.Header().Set("Access-Control-Allow-Origin", "*") 会失败?
因为浏览器在请求带 Cookie 或 Authorization 时,Access-Control-Allow-Origin 不能为 "*",否则直接拒收响应。哪怕你只在开发环境用 localhost:3000,只要前端发请求时加了 credentials: 'include',就必须显式返回匹配的 Origin 值。
常见错误现象:GET 请求能通,POST 报错 Response to preflight request doesn't pass access control check —— 这说明预检(OPTIONS)没被正确响应,或响应头冲突。
-
AllowCredentials: true时,AllowOrigins列表里绝不能含"*" - Origin 是完整协议+域名+端口(如
"http://localhost:3000"),不是只写域名 - 中间件必须在所有路由注册前调用
r.Use(corsMiddleware),否则部分路由不生效
怎么写一个最小可用、不依赖第三方的 CORS 中间件?
核心就三件事:处理 OPTIONS 预检、设置必要响应头、放行后续逻辑。不用 gin-contrib/cors 也能跑得稳,关键是状态码和顺序。
示例中间件:
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
origin := c.Request.Header.Get("Origin")
if origin != "" {
// 白名单校验(生产建议动态查库或配置)
allowed := []string{"http://localhost:3000", "https://app.example.com"}
for _, a := range allowed {
if origin == a {
c.Header("Access-Control-Allow-Origin", origin)
break
}
}
}
c.Header("Access-Control-Allow-Credentials", "true")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
// 关键:OPTIONS 必须返回 204,且立刻终止
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
- 必须用
c.AbortWithStatus(204),不是200;RFC 明确要求预检成功返回204 No Content -
c.Header()比c.Writer.Header().Set()更安全,避免重复 Set 导致 header 覆盖 - 不要在
c.Next()前写任何c.JSON()或c.String(),否则 OPTIONS 会卡住
用 gin-contrib/cors 时哪些配置最容易踩坑?
这个包封装得方便,但默认行为和文档描述有偏差,尤其在白名单和凭证组合场景下。
典型错误配置:
config := cors.Config{
AllowOrigins: []string{"*"},
AllowCredentials: true,
}
这会导致浏览器静默拒绝——AllowOrigins 含 "*" 时,AllowCredentials: true 会被框架忽略或报错,最终响应头不包含 Access-Control-Allow-Origin。
- 正确写法是显式列出 Origin:
[]string{"http://localhost:3000", "https://prod.example.com"} - 若需动态判断(比如从数据库读白名单),别用
AllowOrigins,改用AllowOriginFunc -
AllowAllOrigins: true和AllowCredentials: true不能共存,否则启动时报 panic -
ExposeHeaders只在需要前端 JS 读取自定义响应头(如X-Total-Count)时才设,否则不用填
为什么 OPTIONS 请求没响应,但控制台看不到错误?
这是最隐蔽的问题:浏览器发了 OPTIONS,服务端没返回 204,而是继续走后续 handler(比如进了某个 POST 路由),结果返回了 HTML 或 404,但浏览器认为预检失败,直接中断流程,后续请求根本不发。
验证方式:用 curl 手动发 OPTIONS 请求:
curl -X OPTIONS \ -H "Origin: http://localhost:3000" \ -H "Access-Control-Request-Method: POST" \ -H "Access-Control-Request-Headers: Content-Type" \ http://localhost:8080/api/login -i
- 期望看到
HTTP/1.1 204 No Content+ 正确的 CORS 头 - 如果返回
200 OK或其他状态码,说明中间件没拦截住 OPTIONS - 如果 headers 里没有
Access-Control-Allow-Origin,检查中间件是否注册在r.Use()且早于所有r.POST()
真正麻烦的从来不是写几行 header,而是 OPTIONS 是否被干净截断、Origin 是否被精确匹配、以及凭据开关和通配符是否互相打架。这些点漏掉任何一个,前端就只能看到“跨域失败”四个字,背后却要翻半小时日志。











