go 的 net/http 包不处理 cors,需手动或用 gorilla/handlers.cors 等方案显式控制响应头;手写中间件易因未处理 options、header 设置过晚、credentials 与 * 冲突等问题导致 404 或静默失败。

Go 的 net/http 包完全不处理 CORS,所有响应头都得你显式控制;用错方式会导致 OPTIONS 404、带 cookie 请求静默失败、或浏览器根本收不到响应——这不是配置“没生效”,是压根没走到浏览器可接受的规范路径上。
为什么手写 corsMiddleware 容易 404 或静默失败
看似只加几行 w.Header().Set() 就能跑通,但实际运行时极易掉坑里:
-
OPTIONS请求返回 404:因为http.DefaultServeMux对OPTIONS方法无默认路由逻辑,也不会 fallback 到你的 handler,必须显式注册或由中间件拦截 -
Access-Control-Allow-Origin: "*"和Access-Control-Allow-Credentials: "true"同时存在 → 浏览器强制丢弃整个响应,Network 面板里连 status 都看不到 - header 设置太晚:在
w.WriteHeader()或首次w.Write()之后调用Set(),完全无效 - 漏设
Access-Control-Allow-Headers:前端发了X-Auth-Token,但后端没声明允许 → 预检直接 403,错误提示明确指出 header 不在白名单中
用 gorilla/handlers.CORS 是最稳的通用方案
它不依赖框架、轻量无副作用,覆盖所有预检逻辑和 header 设置时机,适合原生 net/http、gorilla/mux 等场景:
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
- 安装:
go get github.com/gorilla/handlers - 基础用法:
http.ListenAndServe(":8080", handlers.CORS(handlers.AllowedOrigins([]string{"https://myapp.com"}))(r)) -
AllowedOrigins([]string{"*"})时,handlers.AllowCredentials()会被自动禁用——这是对浏览器规范的主动校验,不是 bug - 需要暴露自定义 header(如
X-Request-ID)?加handlers.ExposedHeaders([]string{"X-Request-ID"}) - 动态白名单?改用
handlers.AllowedOriginsFunc(func(origin string) bool { return inWhitelist(origin) })
GIN 用户必须用 gin-contrib/cors,别手写 c.Header()
GIN 的 c.Writer 是封装过的,手写 header 极易绕过中间件链,导致 OPTIONS 响应不全、credentials 冲突或 header 覆盖失效:
- 安装:
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://myapp.com", "http://localhost:3000"},不能含"*" - Nginx 后面跑 Gin?必须透传 header:
add_header 'Access-Control-Allow-Origin' '$sent_http_access_control_allow_origin' always;,否则上游设置的 header 会被清空
手动中间件仅适合极简调试,且必须满足三个硬条件
如果你坚持手写,以下三点缺一不可,否则必出问题:
- 必须在
w.WriteHeader()或w.Write()之前设置所有 CORS header - 必须显式处理
OPTIONS:判断r.Method == "OPTIONS"后立即w.WriteHeader(http.StatusNoContent)并return,不能继续执行业务逻辑 -
Access-Control-Allow-Origin若需支持 credentials,就不能用"*",得从r.Header.Get("Origin")读取并做白名单校验后原样回写
真正容易被忽略的是 header 设置时机和 Nginx 透传行为——哪怕代码里写了 10 行 Set(),只要有一行在 WriteHeader 之后,或者 Nginx 配置里漏了 always 标志,整个 CORS 就算“配置成功”也形同虚设。










