必须用 gin-contrib/cors,因其自动拦截 OPTIONS、校验 Origin 白名单、精准控制 header 注入时机;手写中间件易因 * 与 credentials 冲突、漏设 AllowHeaders 或 header 设置过晚导致静默失败。

别用“学习语言”当借口绕开跨域本质——Go 里处理 CORS 不是语法练习,而是对 HTTP 协议、浏览器安全机制和中间件执行时机的实操校验。写错一行 w.Header().Set() 或漏掉 OPTIONS 分支,前端就收不到响应体,连错误都看不到。
为什么手写 c.Header().Set() 在 Gin 里大概率失败
手写中间件看似可控,实际踩坑密集:
-
Access-Control-Allow-Origin设成"*",同时又开了AllowCredentials: true,浏览器直接静默丢弃响应,Network 面板里 status 是 200,但 response body 为空、headers 里也看不到你设的头 - 没在
if r.Method == "OPTIONS"分支里提前return,导致预检请求继续往下走 Gin 路由,最终 404 或 500 -
AllowHeaders漏了前端实际发的字段,比如X-Auth-Token,预检直接 403,连业务 handler 都没进 - 在
c.JSON()之后再调c.Header().Set(),Go 的 http.ResponseWriter 已 flush,设置无效
必须用 gin-contrib/cors 的三个硬性理由
它不是“可选库”,而是生产环境的兜底保障:
- 自动拦截并响应
OPTIONS请求,不依赖你在每个路由里手动判断r.Method - 校验
Origin是否在AllowOrigins白名单中,拒绝非法来源,避免*引发的凭证冲突 - 精准控制 header 设置时机,在
c.Next()前完成所有 CORS 头注入,不被后续逻辑覆盖
安装:go get github.com/gin-contrib/cors;注册位置:必须在 r := gin.Default() 之后、任何 r.GET() 之前调用 r.Use(cors.New(...))。
立即学习“go语言免费学习笔记(深入)”;
cors.New() 配置项不能抄默认值
cors.Default() 只适合本地调试,生产环境必须显式配置:
-
AllowOrigins必须是完整协议+域名+端口列表,如[]string{"https://myapp.com", "http://localhost:3000"};填"*"且开启AllowCredentials会直接失效 -
ExposeHeaders要显式列出前端 JS 实际要读的字段,比如"X-Total-Count",没列的即使后端返回了也拿不到 -
MaxAge建议设为12 * 60 * 60(12 小时),否则每次非简单请求都触发预检,增加延迟
Nginx 后面跑 Gin 时 header 会被清空
Gin 返回了正确的 Access-Control-Allow-Origin,但浏览器仍报错?大概率是 Nginx 拦截了响应头:
- Nginx 默认不透传自定义响应头,尤其用了
add_header指令后,会清空上游(Gin)已设的所有 header - 必须在 Nginx 配置里显式启用透传:
proxy_pass_request_headers on;,并用add_header补全缺失头(但注意顺序和覆盖逻辑) - 最稳妥做法:让 Gin 直接暴露到公网,或确认 Nginx 的
proxy_hide_header没屏蔽 CORS 相关字段
跨域不是“加几行 header 就完事”的语法题,它是 HTTP 协议层、浏览器策略层、反向代理层三者交叠的执行链——任一环节断掉,前端就卡在 Network 面板里查不到原因。


















