Go CORS问题本质是未提供合规OPTIONS响应:必须显式处理OPTIONS请求,且Access-Control-Allow-Origin、Methods、Headers三头缺一不可、须在WriteHeader前设置,禁止Origin为*时启用Credentials。

Go 语言的 CORS 问题本质不是“配不配得上”,而是“有没有让浏览器拿到合规的 OPTIONS 响应”——没显式处理 OPTIONS,或响应头漏设、设晚、值冲突,都会导致预检失败,且错误常静默(Network 面板里 OPTIONS 状态码是 200,但控制台仍报 CORS error)。
为什么 OPTIONS 请求返回 404 或 405
Go 的 http.DefaultServeMux 和多数路由库(如 gorilla/mux、gin)默认不注册 OPTIONS 路由。浏览器发预检时只用 OPTIONS 方法、无 body、路径完全一致,若没匹配到 handler,就直接 fallback 到 404(未注册)或 405(注册了其他方法但没包含 OPTIONS)。
-
http.HandleFunc("/api/user", handler)不会响应OPTIONS /api/user,必须额外写http.HandleFunc("OPTIONS", ...)或改用通配路径 -
gorilla/mux中只写r.HandleFunc("/api/user", h).Methods("GET", "POST"),需显式补上.Methods("GET", "POST", "OPTIONS") -
gin中router.Any()不覆盖OPTIONS,必须单独调用router.OPTIONS("/api/user", h) - 中间件拦截时,必须在
w.WriteHeader()前判断r.Method == "OPTIONS"并return,否则后续逻辑可能 panic(比如读r.Body)
Access-Control-Allow-* 头必须全设对且时机正确
浏览器校验预检响应时,三个头缺一不可,且必须在首次 w.Write() 或 w.WriteHeader() 之前设置,否则整个 header 被忽略,无任何提示。
-
Access-Control-Allow-Origin:若前端带credentials: "include"(如 cookie、Authorization),此处不能为"*",必须是具体 origin(如"https://myapp.com"),否则浏览器直接丢弃响应 -
Access-Control-Allow-Methods:列出真实请求可能用的方法,逗号+空格分隔,如"GET, POST, PUT";注意OPTIONS可不列,但列了也无害 -
Access-Control-Allow-Headers:必须包含前端fetch或axios中headers字段出现的所有自定义头,大小写敏感,如"X-Auth-Token, Content-Type";漏一个,预检就 403
手写中间件 vs gorilla/handlers.CORS 怎么选
手写看似简单,但极易踩坑;gorilla/handlers.CORS 是生产环境更稳的选择,它自动覆盖预检逻辑、确保 header 设置时机、返回 204 No Content(规范推荐),且不依赖框架。
立即学习“go语言免费学习笔记(深入)”;
- 手写中间件常见失效点:
w.Header().Set()写在w.WriteHeader()后、没拦截OPTIONS、Access-Control-Allow-Origin与Access-Control-Allow-Credentials: "true"同时设为"*" -
gorilla/handlers.CORS必须传入handlers.AllowedOrigins([]string{"https://a.com"})等配置,不能只传空 slice;AllowedOrigins([]string{"*"})是非法用法,应改用handlers.AllowedOrigins([]string{"https://a.com", "https://b.com"})或用handlers.AllowedOrigins([]string{"http://localhost:3000"})开发时临时放行 - 若用 Nginx 反向代理,还需确认它透传了
Access-Control-Allow-*头,否则 Go 服务回了也没用
带 credentials 的请求为什么没进 Network 面板
这不是后端没响应,而是浏览器在预检通过前就终止了流程——只要 Access-Control-Allow-Origin 是 "*" 且前端发了 credentials: "include",浏览器连真实请求都不会发,OPTIONS 响应体也会被丢弃,Network 面板里只看到一个状态码为 200 的 OPTIONS 请求,但 Response 标签为空,控制台报错明确指出 Access-Control-Allow-Origin 不能为通配符。
- 检查前端
fetch是否含{ credentials: "include" },或axios是否设了withCredentials: true - 后端对应响应中,
Access-Control-Allow-Origin必须和请求头Origin完全一致(协议+域名+端口),且不能加路径 - 同时必须设置
Access-Control-Allow-Credentials: "true",否则浏览器认为凭证不被允许
最易被忽略的是 header 设置时机和 credentials 与 * 的互斥性——这两处出错,不会报错日志,不会 panic,但请求就是卡在预检,连业务代码的 log.Println 都不会执行。


















