手动写 Header 易出错主因是未处理 OPTIONS 预检请求,导致 CORS 头缺失或冲突;需显式响应 204,严格匹配 Origin 与 Credentials,动态校验用 rs/cors 更稳妥。

为什么手动写 Header 容易出错
很多人第一反应是直接在 handler 里 w.Header().Set(),但实际跑起来常遇到:前端报错“CORS header ‘Access-Control-Allow-Origin’ missing”,或者带 cookie 的请求被静默拦截。根本原因是没处理 OPTIONS 预检——浏览器对非简单请求(比如含 Authorization 头或 PUT 方法)会先发一次 OPTIONS 请求,而你的 handler 如果没匹配到、没返回头、甚至没提前 return,整个链路就断了。
- 必须显式响应
OPTIONS请求,状态码建议用204 No Content(比200 OK更规范) -
Access-Control-Allow-Origin设为"*"时,Access-Control-Allow-Credentials不能设为"true",否则浏览器直接拒绝 - 如果前端用了
credentials: 'include',Origin 就不能是通配符,得写死成"https://myapp.com"或用函数动态判断 - 漏掉
Access-Control-Allow-Headers中的某个字段(比如X-Request-ID),预检就会失败
gorilla/handlers 和 rs/cors 该怎么选
两个库都成熟,但定位不同:gorilla/handlers 更轻量、API 直观,适合中小型项目快速上线;rs/cors 功能更全(支持 AllowOriginFunc、MaxAge、Debug 模式),且和 Gin/Echo 等框架兼容性更好,生产环境更稳妥。
- 用
gorilla/handlers:适合只配固定域名、方法、头字段的场景,代码干净,调试日志少 - 用
rs/cors:需要白名单动态校验(比如从数据库查允许的 origin)、要开Debug: true查预检失败原因、或想统一控制MaxAge缓存时间时,它更合适 - 两者都不支持“部分路由开启 CORS”,必须包装整个 mux/router;如需细粒度控制(比如
/api/public放开、/api/admin严格限制),得自己写中间件或用框架原生能力(如 Gin 的gin.HandlerFunc)
rs/cors 的几个关键配置项怎么填
rs/cors 看似简单,但几个字段组合容易踩坑。比如 AllowedOrigins 写 []string{"*"} 是合法的,但它和 AllowCredentials 互斥;又比如 AllowedHeaders 设为空 slice 会导致预检失败——因为浏览器默认会带 Origin 和 Access-Control-Request-Method,而库不会自动补上。
-
AllowedOrigins:生产环境别写["*"],换成具体域名数组,如["https://app.example.com", "https://staging.example.com"] -
AllowedHeaders:至少包含"Content-Type"和前端实际用的自定义头,比如"Authorization"、"X-CSRF-Token";设为handlers.AllowedHeaders([]string{"*"})在rs/cors中不生效,得用cors.DefaultHeaders或明确列出 -
AllowCredentials:启用前确认前端是否真需要 cookie 或 auth token;启用后必须删掉"*",并确保每个 origin 都是完整协议+域名 -
MaxAge:设成3600(1小时)能减少重复预检,但改配置后客户端缓存可能延迟生效,调试时可先设小点
本地开发 vs 生产环境的配置差异
开发时图省事用 "*" 和 AllowCredentials: true 组合,结果一上线就 401——这是最典型的环境错配。本质不是 Go 问题,而是浏览器策略在不同 Origin 下行为一致,但你的配置没跟上。
立即学习“go语言免费学习笔记(深入)”;
-
开发环境:可用
AllowedOrigins: []string{"http://localhost:3000", "http://127.0.0.1:3000"},加Debug: true看日志 - 生产环境:禁止
"*",origin 必须精确匹配(包括https://和末尾斜杠);关闭Debug;考虑加Vary: Origin头(rs/cors默认加,gorilla/handlers不加,需手动补) - CDN 或反向代理(如 Nginx)后面部署时,注意它们可能覆盖或删除你写的 CORS 头,得检查响应实际发出的 header
Access-Control-Max-Age 导致高频预检,或是 Vary: Origin 缺失引发 CDN 缓存污染——这些问题在线上才暴露,但根子在本地配置没对齐规范。










