Cookie读取失败主因是浏览器策略拦截与配置不匹配:SameSite/Secure设置不当、domain/path不精确、反向代理修改Cookie头、中间件提前读取Request.Body,以及HttpOnly限制前端JS访问但不影响后端读取。

Cookie读取失败时,先确认SameSite和Secure是否匹配当前请求场景
Kratos本身不直接操作Cookie,而是通过底层HTTP框架(如Go标准库或Gin)设置和读取,但它的会话和身份验证逻辑高度依赖Cookie行为。读取失败最常见原因是浏览器策略拦截——尤其是跨域iframe或非HTTPS环境里设置了SameSite=None却没配Secure。Chrome 80+默认把SameSite当作Lax,而Kratos的登录回调、session恢复等流程若发生在子请求(比如iframe加载、AJAX重定向)中,Lax会直接拒绝发送Cookie。
检查方式很简单:打开浏览器开发者工具 → Application → Cookies,看目标Cookie是否存在、domain/path是否匹配、SameSite值是什么、有没有打上Secure标记。如果Kratos部署在HTTP环境但配置了secure: true,那这个Cookie根本不会被浏览器保存,自然读不到。
- 开发阶段可临时设
same_site: lax或same_site: strict绕过跨域问题(仅限测试) - 生产必须用HTTPS,并显式配置
same_site: none+secure: true - domain字段要精确匹配:前端是
app.example.com,后端Cookie的domain就不能写成.example.com(除非明确需要子域名共享)
Kratos的Session Cookie名和路径不匹配导致读取为空
Kratos默认使用ory_kratos_session作为Cookie名,且路径为/。但如果在Docker或反向代理(如Nginx)后面运行,而代理层做了路径重写(例如把/auth映射到Kratos服务),就可能出现Cookie写入路径是/auth/,但后续请求发往/self-service/...时浏览器不携带该Cookie——因为path不匹配。
验证方法:用curl -v请求Kratos的/self-service/login/browser,看响应头是否有Set-Cookie: ory_kratos_session=...; Path=/auth/; ...。如果有,说明Kratos误判了base path。
- 通过
COOKIE_DOMAIN和COOKIE_PATH环境变量强制指定(不是config.yaml里的cookies.domain,那个只影响UI跳转) - 确保反向代理不修改
Cookie头,Nginx需加proxy_cookie_path /auth/ /; - 如果用了自定义路由前缀(如
/kratos/),必须同步设置COOKIE_PATH=/kratos/,否则浏览器按路径隔离规则直接忽略
中间件或自定义Handler提前读取了Request.Body,导致Kratos解析Cookie失败
Kratos的HTTP handler依赖原始*http.Request对象来提取Cookie。如果你在Kratos之前挂了自定义中间件(比如日志、鉴权、body解密),又调用了r.Body.Read()或io.ReadAll(r.Body),就会消耗掉request body流——而Go的http.Request.Cookies()内部其实也依赖一次完整的请求解析,某些情况下会间接触发body读取(尤其当使用ParseMultipartForm时)。
现象是:Kratos返回401 Unauthorized,但日志里没有明显错误;手动用curl -b "ory_kratos_session=xxx"能成功,说明Cookie本身有效,只是Kratos没拿到。
- 避免在Kratos前的中间件中调用
r.ParseForm()、r.MultipartReader()或任何会触发声明式body读取的操作 - 如果必须读body,请用
httputil.DumpRequest做只读快照,或用io.TeeReader把body内容复制到缓冲区再重置r.Body - Kratos v1.0+已默认禁用自动form解析,但旧版或自定义handler仍可能触发
Cookie被HttpOnly限制,前端JS无法读取但Kratos后端不受影响
这不是Kratos的问题,但常被误认为“读取失败”。Kratos默认把ory_kratos_session设为HttpOnly(这是安全必需),意味着JavaScript无法通过document.cookie访问它——但这完全不影响Kratos后端逻辑,因为服务端读取的是HTTP请求头里的Cookie字段,不经过JS。
如果你在前端调试时发现document.cookie里找不到ory_kratos_session,别慌,只要Network面板里每个请求的Request Headers → Cookie里有它,Kratos就能正常识别用户。
- 前端不需要、也不应该尝试读取或操作这个Cookie
- 若需在前端传递会话状态,应使用Kratos提供的
/sessions/whoami接口获取当前用户信息 - 强行关闭
http_only会极大增加XSS攻击风险,不建议为调试目的修改
真正棘手的点往往藏在代理层和浏览器策略交界处:Nginx改写了Set-Cookie的Path或Domain,而Kratos配置里又没对齐;或者前端页面在http://localhost:3000运行,但Kratos跑在https://auth.example.com,连SameSite=Lax都过不去。这些地方没报错,只有静默失效。











