必须用 gin-contrib/cors 而非手写中间件,因其自动处理 OPTIONS 预检、校验 origin 白名单、正确设置 header 时机;cors.Default() 仅适用于开发,生产环境须用 cors.New() 显式配置 AllowOrigins、AllowCredentials 等。

直接用 gin-contrib/cors,别手写 c.Header() —— 否则 OPTIONS 预检漏处理、AllowCredentials 和 * 冲突、header 设置时机错一次就白配。
为什么不能自己写中间件设 header
手写中间件看似可控,但真实项目里高频踩坑:预检请求(OPTIONS)没单独拦截,导致后端返回 200 + body,浏览器直接报错;Access-Control-Allow-Origin 写成 "*" 却又开了 AllowCredentials: true,浏览器静默丢弃响应;header 在 c.Next() 之后才写, Gin 已经 commit response,header 被忽略。
这些不是“可能出错”,而是只要上线跑几天,必现。官方 gin-contrib/cors 封装了完整流程:自动识别并响应 OPTIONS、校验 origin 白名单、按需注入 ExposeHeaders、兼容中间件链执行顺序。
安装命令就是:go get github.com/gin-contrib/cors,无额外依赖。
cors.New() 生产环境必须显式配置
cors.Default() 只适合本地开发:它隐式禁用 AllowCredentials,AllowOrigins 是 ["*"],MaxAge 为 0,每次请求都触发预检,延迟高且不安全。
上线前必须改用 cors.New(config),关键项要明确赋值:
-
AllowOrigins必须是完整域名切片,如[]string{"https://myapp.com", "http://localhost:3000"};不支持"https://*.example.com"这类通配符匹配 -
AllowCredentials: true时,AllowOrigins里绝不能含"*",否则浏览器直接拒收 -
ExposeHeaders要列前端 JS 真正要读的字段,比如"X-Total-Count"或"New-Token";没列的,即使后端返回了也拿不到 -
MaxAge建议设为12 * time.Hour,减少重复预检
Nginx 后面跑 Gin?header 很可能被吞了
Gin 返回了正确的 Access-Control-Allow-Origin,但浏览器仍报错?90% 是 Nginx 拦截或覆盖了 header。
Nginx 默认不透传自定义响应头,尤其当你用了 add_header,它会清空上游(Gin)已设的所有 header。
必须确保:
- Nginx 配置中所有
add_header都带always参数,例如add_header Access-Control-Allow-Origin "*" always; -
OPTIONS请求不能被 Nginx 直接return 204,必须透传给 Gin 处理,否则预检失败 - 检查 Nginx error log,如果出现
upstream sent no valid HTTP/1.0 header,说明 Gin 在写 header 前就写了 body(比如日志中间件提前c.AbortWithStatusJSON),导致 header 无法再设
带 cookie 的请求失败?先盯住这三点
前端加了 withCredentials: true,后端 AllowCredentials: true,还是失败——问题往往不在 Gin 本身:
- 确认前端发请求时,
Origin头和后端AllowOrigins列表中的某一项完全一致(协议、域名、端口缺一不可) - 检查浏览器开发者工具 Network 面板,看响应头里有没有
Access-Control-Allow-Credentials: true和匹配的Access-Control-Allow-Origin(不能是*) - 如果用了 Nginx,确认它没把
Set-Cookie或Access-Control-Allow-Origin给吞掉或重写
动态 origin(比如从数据库查白名单)不适合 gin-contrib/cors 的默认逻辑,这时才考虑自己写中间件,但得手动处理 OPTIONS、origin 校验、header 注入三件事,不能只抄个 c.Header() 模板。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











