网关层统一配置cors更关键,因它是所有请求入口,可避免各服务重复实现、漏配或冲突;必须拦截并响应options请求,禁止转发,且allowcredentials为true时allowedorigins不能为"*",须用具体域名或动态白名单校验。

Go微服务API网关中配置CORS,不能只套用AllowedOrigins: []string{"*"}就完事——它在生产环境会直接导致Access-Control-Allow-Credentials失效、前端拿不到Cookie,且无法通过浏览器预检校验。
为什么网关层配CORS比服务层更关键
API网关是所有请求的统一入口,前置处理CORS能避免每个后端服务重复实现、漏配或策略冲突。尤其当后端服务使用不同框架(如Gin/Kratos/HTTP)时,网关统一拦截OPTIONS并注入头,才能确保预检请求不穿透到下游服务(否则可能返回404或502)。
- 网关必须响应
OPTIONS请求,且不能转发给下游;否则浏览器收不到Access-Control-Allow-Methods等头,直接报CORS error: Response to preflight request doesn't pass access control check - 若下游服务也配了CORS,可能出现响应头重复(如两个
Access-Control-Allow-Origin),触发浏览器拒绝策略 - 网关可基于路由路径动态匹配Origin,比如
/admin/*只放行https://admin.example.com,而/api/*放行多个前端域
rs/cors vs gorilla/handlers:选哪个?
两者都成熟,但行为差异直接影响调试体验:
-
rs/cors默认不自动处理OPTIONS,需显式设置OptionsPassthrough: true,否则网关会把预检请求转发下去——这是最常被忽略的坑 -
gorilla/handlers.CORS()默认拦截并响应OPTIONS,更省心;但它的AllowedOrigins不支持通配符子域名(如*.example.com),得手动解析Host头做白名单校验 - 如果你用Kratos Gateway,它内置的CORS中间件底层就是
rs/cors,但封装了OptionsPassthrough开关,默认开启,无需额外操作
AllowCredentials为true时,AllowedOrigins不能写"*"
这是硬性限制:浏览器明确禁止Access-Control-Allow-Origin: "*"与Access-Control-Allow-Credentials: "true"共存。一旦启用Cookie或Authorization携带凭证,AllowedOrigins必须是具体值或动态匹配。
- 开发环境可写
[]string{"http://localhost:3000", "http://127.0.0.1:3000"} - 生产环境建议从请求头
Origin提取域名,白名单校验后再回写(避免反射Origin造成安全漏洞) - 示例逻辑:
if origin := r.Header.Get("Origin"); origin != "" { if slices.Contains(allowedOrigins, origin) { w.Header().Set("Access-Control-Allow-Origin", origin) } }
预检缓存(Access-Control-Max-Age)设太高反而坏事
这个头控制浏览器缓存预检结果的时间(单位秒)。设成86400(24小时)看似省事,但会导致一个问题:当你改了AllowedMethods或AllowedHeaders,前端仍沿用旧缓存,直到过期才重新预检——期间所有非简单请求都失败。
- 推荐设为
300(5分钟),平衡性能与灵活性 - 若网关支持热重载配置(如Kratos支持watch YAML),可配合短缓存实现策略秒级生效
- 注意:Chrome对
Access-Control-Max-Age上限是600秒,设更高会被截断
真正棘手的从来不是怎么配,而是配完之后没验证预检路径是否真被网关拦截——用curl -X OPTIONS -H "Origin: https://example.com" -I http://gateway/api/user看响应头,确认Access-Control-Allow-Origin存在且值正确,且状态码是200而非204或404。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











