go http服务器默认不处理跨域,必须显式处理options预检请求并设置完整cors头;带credentials时access-control-allow-origin不能为*,须动态校验白名单域名且补全vary: origin头。

Go 的 HTTP 服务器默认不处理跨域,浏览器在发请求前会先发 OPTIONS 预检,如果没响应或头不对,请求根本不会到达你的业务逻辑——这不是后端“没收到”,是前端被拦在了网关外。
为什么手动写中间件容易漏掉 OPTIONS 处理
常见错误是只在业务 handler 里加 w.Header().Set("Access-Control-Allow-Origin", "*"),但没拦截 OPTIONS 请求。结果浏览器发完预检,收到 404 或 405,直接中断流程。
-
OPTIONS必须返回http.StatusNoContent(204)或http.StatusOK(200),且不能有响应体 - 所有 CORS 头(如
Access-Control-Allow-Methods)必须在OPTIONS响应里也带上,否则预检失败 - 中间件要包裹最终 handler,不是包裹
http.ServeMux本身;否则/api/xxx路由可能绕过中间件
gorilla/handlers.CORS 的两个关键参数必须配对
用 gorilla/handlers 时,AllowCredentials: true 和 AllowedOrigins 不能随意组合,否则浏览器静默拒绝。
- 若设
handlers.AllowedCredentials(true),则handlers.AllowedOrigins([]string{"*"})会触发报错:The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' - 必须用具体域名列表,例如
[]string{"https://app.example.com", "http://localhost:3000"} - 子域名通配(如
"https://*.example.com")不被标准支持,需自己解析Origin并校验
带凭证(credentials: true)时的硬性限制
前端 fetch 设置了 { credentials: 'include' },后端就必须同步满足三项,缺一不可:
-
Access-Control-Allow-Credentials: "true"(字符串值,不是布尔) -
Access-Control-Allow-Origin必须是单个明确域名,不能是*,也不能是逗号分隔的多个域名 - 响应中不能遗漏
Vary: Origin头(gorilla/handlers和rs/cors会自动加,手写中间件需手动补)
漏掉 Vary: Origin 可能导致 CDN 或代理缓存污染,同一个缓存响应被错误地复用于不同源的请求。
动态 Origin 校验为什么不能依赖第三方库的默认配置
gorilla/handlers.CORS 不支持回调函数式匹配,rs/cors 虽然支持 AllowedOriginsFunc,但多数人直接传静态列表,忽略了多租户或灰度发布场景下的真实需求。
- 白名单域名变化频繁时,硬编码列表难维护;建议从配置中心或数据库加载,避免重启服务
- 校验
Origin时注意协议、端口、末尾斜杠差异,比如http://localhost:3000和http://localhost:3000/是两个不同源 - 别用正则粗暴匹配(如
^https?://.*\.example\.com$),防止恶意构造Origin: https://evil.example.com.attacker.com绕过
真正棘手的不是加几个 header,而是 Origin 校验逻辑一旦出错,就等于把 CSRF 防御大门敞开了一条缝——这点常被忽略,直到上线后出现凭证泄露才意识到。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











