kratos gateway 的 cors 配置必须在 config.yaml 中统一设置,因其作为独立网关进程默认拦截 options 请求且不透传下游 cors 头;allowcredentials: true 时 alloworigins 禁用 "*",否则浏览器静默失败。

必须在网关层统一配 CORS,不能依赖下游服务;Kratos Gateway 默认已拦截 OPTIONS,但 allowCredentials: true 时 allowOrigins 不能写 "*" —— 否则前端静默失败,连控制台错误都没有。
为什么 Kratos Gateway 的 CORS 配置必须在 config.yaml 里做
Kratos Gateway 是独立进程,所有流量先经过它再转发。如果只在后端 service(比如 user service)里加中间件配 CORS,网关本身不透传或覆盖响应头,浏览器看到的仍是网关返回的无 CORS 头的响应——尤其预检请求(OPTIONS)根本到不了下游,因为网关默认已拦截并返回 204。
常见错误现象:Response to preflight request doesn't pass access control check,本质是网关没配、或配了但 OptionsPassthrough: true(rs/cors 默认行为,但 Kratos 已设为 false)。
- 确认你用的是 Kratos 官方 gateway 模块(非手写 reverse proxy),它的
cors中间件默认生效且拦截 OPTIONS - 不要在 downstream service 的
middleware里重复加cors,否则可能 header 冲突或重复写入 - 若用
gorilla/handlers.CORS()自定义中间件,需手动确保OptionsPassthrough: false且状态码为http.StatusNoContent
config.yaml 中 cors 配置的关键参数与陷阱
Kratos Gateway 的 CORS 配置位于 cmd/gateway/config.yaml 的 middlewares 下,核心是 allowCredentials 和 allowOrigins 的联动关系。
-
allowCredentials: true时,allowOrigins必须是明确域名列表,如["https://app.example.com", "http://localhost:3000"];写["*"]会导致浏览器直接丢弃响应 - 开发环境可硬编码本地地址,但生产环境建议禁用通配符,改用动态白名单:从请求头
Origin提取 host,校验是否在许可域内,再回写该 Origin 值(避免反射型漏洞) -
allowMethods必须显式包含"OPTIONS",否则预检失败;allowHeaders若含自定义头(如X-Auth-Token),也要列全 - Kratos 默认自动添加
Vary: Origin,不用手动补;但手写中间件时漏掉这个头,CDN 或代理可能缓存错误响应
如何验证 OPTIONS 预检是否被网关正确处理
浏览器发预检请求时,不会带 Cookie 或认证头,只关心响应头和状态码。直接用 curl 模拟最可靠:
curl -I -X OPTIONS \ -H "Origin: https://app.example.com" \ -H "Access-Control-Request-Method: POST" \ -H "Access-Control-Request-Headers: Content-Type,Authorization" \ http://localhost:9000/api/user
期望响应包含:
- 状态码为
204 No Content(Kratos 默认)或200 OK,且响应体为空 - 头中含
Access-Control-Allow-Origin: https://app.example.com(不是*) - 含
Access-Control-Allow-Credentials: "true"(字符串,非布尔值) - 含
Vary: Origin
如果返回 404/405,说明网关没加载 cors 中间件,或 endpoints 路由没匹配上该路径。
动态 Origin 白名单的最小可行实现(Go 代码片段)
Kratos 不内置动态 Origin 校验,需自己扩展中间件。关键点:只在 allowOrigins 设为空切片时触发校验逻辑,避免覆盖默认行为。
func DynamicCORS() middleware.Middleware {
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (interface{}, error) {
if origin := transport.FromServerContext(ctx).Request.Header.Get("Origin"); origin != "" {
// 白名单校验逻辑,例如检查是否在数据库或配置 map 中
if isValidOrigin(origin) {
transport.SetHeader(ctx, "Access-Control-Allow-Origin", origin)
transport.SetHeader(ctx, "Access-Control-Allow-Credentials", "true")
transport.SetHeader(ctx, "Vary", "Origin")
}
}
return handler(ctx, req)
}
}
}
注意:isValidOrigin 必须严格校验协议+域名+端口,拒绝 https://evil.com#@example.com 这类畸形 Origin;且仅对非 OPTIONS 请求生效,OPTIONS 仍由 Kratos 默认 cors 中间件处理。
真正容易被忽略的,是 Access-Control-Allow-Origin 和 Access-Control-Allow-Credentials 的组合必须原子性生效——少一个头,或 Origin 值多一个空格,浏览器就拒绝整个响应,且不报错。











