Kratos Gateway 的 CORS 必须在 config.yaml 中配置,因其作为独立进程默认拦截并响应 OPTIONS 预检请求(返回 204),不转发至下游服务;若下游加中间件无效且易导致头冲突或静默失败,尤其 allowCredentials: true 时 allowOrigins 禁用 "*"。

必须在 Kratos Gateway 的 config.yaml 里配 CORS,下游 service 里加中间件无效,且会导致 OPTIONS 预检失败或响应头冲突。
为什么网关层配置是唯一有效位置
Kratos Gateway 是独立进程,所有请求(包括 OPTIONS)都先经过它。它默认拦截预检请求并返回 204,根本不会转发到下游 service。哪怕你在 user service 里用 gorilla/handlers.CORS() 或自定义中间件加了头,浏览器也收不到——因为响应压根没经过那里。
常见错误现象:Response to preflight request doesn't pass access control check,本质就是网关没配,或配了但没生效。
-
OptionsPassthrough: false必须为 false(Kratos 默认已设,不用改) - 网关的 CORS 中间件默认启用,不要在 downstream service 再注册一套
- 手写中间件时漏掉
Vary: Origin,CDN 或代理可能缓存错误响应
allowCredentials: true 时 allowOrigins 不能写 ["*"]
这是浏览器硬性限制:一旦响应头含 Access-Control-Allow-Credentials: "true",Access-Control-Allow-Origin 就不能是通配符。否则浏览器静默丢弃响应,控制台连错误都不报。
开发环境可写死:
allowOrigins: ["http://localhost:3000", "http://127.0.0.1:3000"]
生产环境必须用动态校验,例如从请求头 Origin 提取 host,查白名单后再回写该值,避免反射型漏洞。
- 显式列出域名,如
["https://app.example.com"] -
allowMethods必须包含"OPTIONS",否则预检直接 405 -
allowHeaders要列全自定义头,比如"X-Auth-Token"
如何验证网关是否正确处理 OPTIONS 预检
浏览器发预检时不会带 Cookie 或认证头,只看响应头和状态码。用 curl 模拟最可靠:
curl -I -X OPTIONS \ -H "Origin: https://app.example.com" \ -H "Access-Control-Request-Method: POST" \ -H "Access-Control-Request-Headers: X-Auth-Token" \ http://localhost:9000/api/v1/users
期望响应包含:
Access-Control-Allow-Origin: https://app.example.com-
Access-Control-Allow-Credentials: true(如果启用了凭证) Access-Control-Allow-Methods: POST, OPTIONSAccess-Control-Allow-Headers: X-Auth-Token-
Status: 204 No Content(且响应体为空)
动态白名单逻辑容易被忽略——它不是“加个函数就行”,而是要处理空 Origin、非法协议(file:、data:)、末尾斜杠不一致等边界情况,并确保 fallback 行为符合预期。否则上线后某天某个灰度前端突然跨不过去,排查成本远高于提前写严一点。











