应直接使用 gorilla/handlers.cors 或 gin-contrib/cors,而非手写 header;因手写易致 options 404、origin 与 credentials 冲突被浏览器静默丢弃、header 设置过晚无效、漏设 allow-headers 等问题,且 nginx 需透传 header 才生效。

直接用 gorilla/handlers.CORS 或 gin-contrib/cors,别手写 header——90% 的跨域失败不是后端没返回数据,而是 OPTIONS 预检被浏览器静默拦截,或 Access-Control-Allow-Origin 与 Access-Control-Allow-Credentials: true 冲突导致响应根本进不了 Network 面板。
为什么手写中间件大概率失效
看似只加几行 w.Header().Set() 就能跑通,但实际运行时极易掉坑里:
-
OPTIONS请求返回 404:因为没注册该 method 的 handler,http.DefaultServeMux不会 fallback 处理预检 -
Access-Control-Allow-Origin: "*"和Access-Control-Allow-Credentials: true同时出现:浏览器强制丢弃整个响应,Network 面板里连 status 都看不到 - header 设置太晚:在
w.WriteHeader()或首次w.Write()之后调用Set(),完全无效 - 漏掉
Access-Control-Allow-Headers:前端带了Authorization或X-Requested-With,但后端没声明允许,预检就失败
gorilla/handlers.CORS 正确用法
适用于原生 net/http、gorilla/mux 等场景,轻量无依赖,覆盖所有预检逻辑和 header 时机控制:
- 安装:
go get github.com/gorilla/handlers - 基础配置(带凭证):
import "github.com/gorilla/handlers" <p>r := mux.NewRouter() // ... 注册路由 http.ListenAndServe(":8080", handlers.CORS( handlers.AllowedOrigins([]string{"<a href="https://www.php.cn/link/bf3e87e214621d9025abdcd1aa8ab2a2">https://www.php.cn/link/bf3e87e214621d9025abdcd1aa8ab2a2</a>", "<a href="https://www.php.cn/link/8e5687e2d6ab87e5da2f833f3e8986a4">https://www.php.cn/link/8e5687e2d6ab87e5da2f833f3e8986a4</a>"}), handlers.AllowCredentials(), handlers.AllowedMethods([]string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}), handlers.AllowedHeaders([]string{"Content-Type", "Authorization"}), handlers.ExposedHeaders([]string{"X-Total-Count", "X-Request-ID"}), handlers.MaxAge(12 <em> 60 </em> 60), )(r))</p> - 注意:
AllowedOrigins([]string{"*"})会自动禁用AllowCredentials(),这是中间件对浏览器规范的主动校验,不是 bug - 动态白名单用
handlers.AllowedOriginsFunc(func(origin string) bool { return inWhitelist(origin) })
GIN 框架必须用 gin-contrib/cors
GIN 的 c.Writer 是封装过的,手写 c.Header().Set() 极易绕过中间件链,导致预检响应不全、header 覆盖或 credentials 冲突:
- 安装:
go get github.com/gin-contrib/cors - 注册位置:必须在
r := gin.Default()之后、任何r.GET()之前调用r.Use(cors.New(...)) - 生产环境禁用
cors.Default():它隐式禁用AllowCredentials,且MaxAge = 0,每次请求都触发预检 - 带 cookie 的请求,
AllowOrigins必须显式列全协议+域名+端口,不能含"*",例如:[]string{"https://www.php.cn/link/bf3e87e214621d9025abdcd1aa8ab2a2", "https://www.php.cn/link/8e5687e2d6ab87e5da2f833f3e8986a4"} - 暴露自定义 header(如
New-Token)必须写进ExposeHeaders,否则 JS 读不到
Nginx 后面跑 Go 服务?必须透传 CORS header
Gin 或 net/http 返回了正确的 Access-Control-Allow-Origin,但浏览器仍报错?八成是 Nginx 拦截或覆盖了响应头:
- Nginx 默认不透传自定义响应头,尤其当用了
add_header,会清空上游已设的 header - 正确做法:用
$sent_http_*变量透传,例如:add_header 'Access-Control-Allow-Origin' '$sent_http_access_control_allow_origin' always; - 禁止在 location 块里漏掉
always参数,否则子块会丢掉父块 header - OPTIONS 请求不能由 Nginx 直接返回 204,必须透传给 Go 处理,否则预检逻辑缺失
最常被忽略的一点:CORS 是浏览器单方面执行的策略,Go 本身不“支持”或“不支持”它;你看到的失败,几乎全是 header 设置时机、值冲突或反向代理透传缺失造成的——不是代码没写,是没写对地方、没写对组合、没写对顺序。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











