生产环境禁用 Access-Control-Allow-Origin: "*",因浏览器禁止其与 credentials 共存;须用 gorilla/handlers.CORS 配置具体域名白名单、显式开启凭据与暴露头,并确保 nginx 透传响应头。

直接用 Access-Control-Allow-Origin: "*" 在生产环境必然出问题,尤其当前端带 cookie 或 token 时,浏览器会静默拦截请求——这不是后端没响应,而是根本没发出去。
gorilla/handlers.CORS 参数配错导致凭据失效
开 AllowCredentials: true 却配 AllowedOrigins: ["*"],中间件会自动禁用凭据支持,前端 fetch 带 credentials: 'include' 时返回 401 或空响应体。
-
AllowedOrigins必须是完整协议+域名+端口列表,例如[]string{"https://app.example.com", "http://localhost:3000"},不支持通配子域(https://*.example.com不合法) -
AllowCredentials()和AllowedOrigins必须同时存在且匹配;单独开启凭据会被忽略 - 如果 Origin 不在白名单里,中间件默认不写任何 CORS 头,浏览器视为拒绝,不会 fallback 到
*
OPTIONS 预检请求返回 404 或 405
Go 的 net/http 默认不处理 OPTIONS 方法,路由未显式注册或中间件未覆盖该路径时,预检直接 404。浏览器因此拒绝后续真实请求。
- 用
gorilla/handlers.CORS时,它自动注册预检响应逻辑,无需额外路由定义 - 手写中间件必须在
r.Method == "OPTIONS"时立即w.WriteHeader(http.StatusOK)并 return,不能继续调用next.ServeHTTP - 确保中间件包裹的是最终路由处理器(如
handlers.CORS(...)(r)),不是某个子 handler
ExposedHeaders 没设导致前端读不到自定义响应头
即使后端写了 w.Header().Set("X-Request-ID", "abc123"),前端 JS 执行 response.headers.get("X-Request-ID") 仍返回 null——因为没声明暴露。
立即学习“go语言免费学习笔记(深入)”;
-
ExposedHeaders必须显式列出所有前端需要读取的非简单响应头(Content-Type、Cache-Control等除外) - 常见需暴露的头:
"X-Total-Count"、"X-RateLimit-Limit"、"X-Request-ID" - 用
gorilla/handlers.ExposedHeaders([]string{"X-Request-ID"}),不是Set("Access-Control-Expose-Headers", ...)
header 写入时机错误导致 CORS 失效
一旦调用 w.WriteHeader() 或 w.Write() 开始输出响应体,再调用 w.Header().Set() 就完全无效——CORS 头根本不会发出。
- 中间件必须在
next.ServeHTTP之前设置所有 CORS 头(gorilla/handlers已内部保证这点) - 手写中间件里,
w.Header().Set(...)一定要放在if r.Method == "OPTIONS"分支之前,且不能在业务 handler 里重复写 - GIN 用户别在 handler 里用
c.Writer.Header().Set(),应统一走Use()注册中间件
最易被忽略的是:Nginx 反向代理默认不透传 Access-Control-Allow-Origin 等响应头,上线后 CORS 突然失效,往往不是 Go 代码问题,而是 Nginx 缺少 add_header 或 proxy_pass 后漏了 proxy_hide_header 配置。


















