网关层必须拦截options预检请求,否则转发至下游将导致404/502错误;allowcredentials为true时allowedorigins不可设为"*",且需按路径动态匹配白名单并避免响应头重复注入。

网关层必须拦截OPTIONS请求,不能转发
浏览器发起跨域请求前,会先发一个OPTIONS预检请求。如果网关不拦截而直接转发给下游服务,下游可能返回404或502,导致前端报错“Response to preflight request doesn't pass access control check”。这和后端是否配了CORS无关——只要预检没被网关响应,整个链路就断了。
-
rs/cors默认不处理OPTIONS,必须显式设OptionsPassthrough: false(或true取决于版本,Kratos Gateway已封装并默认开启) -
gorilla/handlers.CORS()默认拦截并响应OPTIONS,更省心,但需确认你用的版本行为一致 - 检查中间件顺序:CORS中间件必须在路由匹配之前注册,否则
/admin/*这类路径级策略无法生效
AllowCredentials为true时,AllowedOrigins不能写"*"
这是硬性限制,不是建议。一旦前端设置了credentials: true(比如带Cookie或Authorization头),浏览器会直接拒绝响应,哪怕后端返回了Access-Control-Allow-Credentials: "true",只要Access-Control-Allow-Origin是"*"就无效。
- 开发环境可硬编码:
[]string{"http://localhost:3000", "http://127.0.0.1:3000"} - 生产环境必须动态提取
r.Header.Get("Origin"),再比对白名单(如slices.Contains(allowedOrigins, origin)) - 禁止反射Origin回写(如
w.Header().Set("Access-Control-Allow-Origin", origin)无校验),这等于开放任意域名,存在CSRF风险
按路由路径动态匹配Origin白名单
不同前端域访问不同API路径时,不能一刀切配同一个Origin列表。比如/admin/*只允许https://admin.example.com,而/api/*要放行https://web.example.com和https://mobile.example.com。
- 用
gorilla/mux或chi等支持路径匹配的路由器,配合中间件做r.URL.Path前缀判断 - 避免在每个handler里重复写Origin校验逻辑,统一放在网关CORS中间件中,根据路径查对应白名单
-
rs/cors本身不支持路径级Origin,得自己封装一层:先路由匹配,再调用cors.New传入不同AllowedOrigins配置
多个服务混用时,避免响应头重复注入
如果网关配了CORS,下游服务(比如Gin/Kratos)又自己加了一套,会出现两个Access-Control-Allow-Origin头。浏览器会直接拒绝整个响应,且控制台只报“CORS error”,不提示具体哪一行重复。
- 网关配完CORS后,下游服务必须关闭所有CORS中间件(Gin的
cors.Default()、Kratos的middleware.CORS()等) - 用curl测试真实响应头:
curl -I -H "Origin: https://example.com" http://gateway/api/xxx,确认只有网关注入的头 - 若下游有遗留服务无法改,可在网关层用
Header().Del()清除重复头(但治标不治本,优先清理下游)
OPTIONS是否真被网关响应、以及AllowCredentials启用后AllowedOrigins是否仍写着"*"——这两个点出错,前端控制台几乎不报明确错误,只能靠抓包看响应头或复现预检请求。











