Kratos会话Cookie默认HttpOnly且Secure,前端无法通过document.cookie读取;唯一安全读取方式是调用/sessions/whoami等受保护端点,由后端校验并返回身份数据。

Kratos 的 Cookie 读取不是靠前端 document.cookie
Kratos 本身不提供客户端 JavaScript API 来读写其会话 Cookie。它的会话 Cookie(如 ory_kratos_session)默认设为 HttpOnly: true 和 Secure: true,这意味着:document.cookie 根本读不到它,XMLHttpRequest 或 fetch 也不允许 JS 访问。这不是 bug,是安全设计。
真正能“读取” Kratos 会话的方式只有一种:向 Kratos 的受保护端点(如 /sessions/whoami)发起带凭证的请求,由 Kratos 后端校验 Cookie 并返回用户身份数据。这个过程完全绕过前端解析 Cookie 字符串。
- 前端必须在请求中带上
credentials: "include"(Fetch)或withCredentials: true(Axios),否则浏览器压根不发送该 Cookie - 若用 curl 或 Postman 测试,需手动加
-b "ory_kratos_session=xxx",且确保Secure属性与当前协议匹配(HTTP 下设Secure: false会静默丢弃) -
/sessions/whoami返回的是解析后、已校验过的会话主体(JSON),不是原始 Cookie 值
Kratos 写入 Cookie 的配置项全在 kratos.yml 里
Kratos 不通过中间件代码调用 ctx.cookies.set 这类方式写 Cookie,所有 Cookie 行为由服务启动时加载的 YAML 配置驱动。关键字段集中在 selfservice.whitelist 和 cookies 节点下:
-
cookies.domain必须设为你的主域(如example.com),不能是www.example.com或auth.example.com单独写——否则子域间无法共享会话 -
cookies.same_site推荐设为Lax(默认值),既防 CSRF,又兼容常规导航;只有 OAuth 回调等明确需跨站 POST 的场景才设None,且必须同步开启cookies.secure: true -
cookies.path通常保持/,避免因路由嵌套导致部分路径拿不到 Cookie -
cookies.max_age应与会话 TTL 对齐(如24h),不要依赖expires字段,Kratos 内部优先用 Max-Age
改完配置必须重启 Kratos 实例,热重载不生效。漏掉 domain 或协议不匹配(HTTPS 服务却配 secure: false)是生产环境 Cookie 写入失败的头号原因。
为什么 /self-service/login/browser 返回 302 却没 Set-Cookie?
这是 Kratos 最常被卡住的问题:前端跳转到登录页一切正常,填完表单 POST 到 /self-service/login/methods/password,响应却是 302 重定向,但浏览器 Network 面板看不到 Set-Cookie 头,后续请求始终 401。
根本原因不在 Kratos 逻辑,而在反向代理(Nginx / Traefik)或 CDN 拦截并剥离了 Set-Cookie 响应头:
- Nginx 默认会过滤掉
Set-Cookie中的SameSite=None(旧版本)或Secure属性不匹配的头 - Traefik 若未显式配置
passHostHeader: true和forwardedHeaders.trustedIPs,可能污染X-Forwarded-Proto,导致 Kratos 错判 HTTPS 环境而拒绝写 Secure Cookie - Cloudflare 等 CDN 在 “Browser Integrity Check” 或 “Hotlink Protection” 开启时,会静默丢弃含敏感属性的 Cookie 头
验证方法:绕过代理直连 Kratos 端口(如 curl -v http://localhost:4433/self-service/login/methods/password),看响应头是否有 Set-Cookie: ory_kratos_session=...。有则问题出在代理层。
多子域共享会话时,SameSite 和 Domain 的组合陷阱
当你的站点横跨 app.example.com、auth.example.com、api.example.com 时,Kratos 的 Cookie 必须同时满足两个条件才能被三者共用:
-
cookies.domain设为.example.com(注意开头的点),而非example.com或www.example.com -
cookies.same_site不能为Strict,否则从app域发起的跳转到auth域不会携带 Cookie;Lax是平衡安全与可用性的合理选择 - 所有子域必须使用同一协议(全 HTTPS),否则
Secure属性会导致 HTTP 子域无法读写
容易忽略的一点:Domain=.example.com 会让浏览器把 Cookie 同时发给 example.com(裸域)和所有子域,但如果你的 Kratos 服务只部署在 auth.example.com,而登录页放在 example.com,那么裸域的请求将无法命中 Kratos,导致会话创建失败——此时应统一入口域名,或用 301 将裸域强制跳转到子域。











