OPTIONS预检请求返回404或405的根本原因是Go的net/http默认不响应OPTIONS方法,必须在中间件中显式判断c.Request.Method=="OPTIONS"并返回204状态码后立即终止链路;同时Access-Control-Allow-Origin设为"*"与AllowCredentials:true冲突,需指定明确白名单域名。

OPTIONS预检请求返回404或405怎么办
根本原因是 Go 的 net/http 默认不响应 OPTIONS 方法,而浏览器在非简单请求(如含 Authorization 头、PUT 方法)前必须收到合法响应,否则直接拦截后续请求。
- 不要依赖路由自动 fallback:哪怕你注册了
GET /api/users,OPTIONS /api/users仍会 404,除非显式处理 - 中间件里必须提前判断
r.Method == "OPTIONS",并调用w.WriteHeader(http.StatusOK)后立即return - 若用
gorilla/handlers.CORS或rs/cors,它们内部已覆盖该逻辑;但手写中间件时漏掉这一行,90% 的跨域失败都源于此 - 注意:不能只对特定路径做 OPTIONS 响应——CORS 是全局策略,中间件需包裹整个 handler,而非单个路由
Access-Control-Allow-Origin设为*却带credentials报错
浏览器强制禁止这种组合:Access-Control-Allow-Origin: * 和 Access-Control-Allow-Credentials: true 同时存在时,会直接拒绝响应,并在控制台报错:The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*'。
- 只要前端 fetch 中写了
credentials: "include"(或 jQuery 的xhrFields: { withCredentials: true }),后端就必须把AllowedOrigins设为明确域名列表,例如[]string{"https://app.example.com", "http://localhost:3000"} -
gorilla/handlers和rs/cors都会在AllowCredentials: true且AllowedOrigins含"*"时 panic,提前暴露问题 - 动态 Origin(如多租户 SaaS)不能靠库自动解决,得自己解析
r.Header.Get("Origin"),再查白名单,匹配成功才设w.Header().Set("Access-Control-Allow-Origin", origin)
用 gorilla/handlers.CORS 时 header 不生效
常见于 header 设置时机错误或中间件包裹顺序不对——CORS 头必须在任何业务逻辑执行前写入,且不能被后续中间件覆盖。
-
handlers.CORS(...)(r)必须是最终 handler,不能放在其他中间件之后(比如认证中间件之后),否则可能被重写或跳过 - 确保
AllowedHeaders显式列出前端实际发送的自定义头,例如Authorization、X-Request-ID;漏掉会导致预检失败 -
ExposedHeaders是可选但关键:前端 JS 想读response.headers.get("X-Total-Count"),后端就必须在 CORS 配置中加handlers.ExposedHeaders([]string{"X-Total-Count"}),否则返回null - MaxAge 单位是秒,设太大(如
86400)会让浏览器缓存预检结果一整天,改配置后前端可能仍走旧策略,调试时建议先设小值(如10)
Gin 框架里直接 c.Writer.Header().Set() 为什么无效
因为 Gin 的 c.Writer 是封装对象,部分底层操作(尤其是 OPTIONS 响应)绕过了标准 Header 写入流程;更严重的是,它不保证在所有执行路径下都生效,尤其遇到 c.Abort() 或异常中断时。
立即学习“go语言免费学习笔记(深入)”;
- 必须用
gin-contrib/cors或自定义中间件函数(func(c *gin.Context)),通过c.Next()控制链路,而不是在某个 handler 里零散写头 - 别在 handler 函数体开头写
c.Header("Access-Control-Allow-Origin", "*")—— 这只对当前路由生效,且无法拦截预检请求 - 如果用了
gin-contrib/cors,注意它默认禁用AllowCredentials,需显式开启:cors.DefaultConfig().AllowCredentials = true,否则带 cookie 的请求照样被拦


















