# 一、CRUD(增删改查)
# 1. 新增 / 修改 (覆盖)
本质上,新增和修改是同一操作。如果指定 key、domain 和 path 的 Cookie 已存在,则覆盖;否则,创建新的。
// 新增或修改一个有效期为 1 小时的 Cookie
document.cookie = "key=value; domain=example.com; path=/; max-age=3600";
- 关键原则:同名 + 同
domain+ 同path⇒ 覆盖。任一属性不一致,则会创建一个全新的 Cookie,导致同名 Cookie 共存。
# 2. 删除
通过将 expires 设为过去的时间点或 max-age 设为 0 或负数来删除 Cookie。
// 删除 Cookie,必须指定确切的 domain 和 path
document.cookie = "key=; domain=example.com; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
- 关键原则:删除时必须提供与待删除 Cookie 完全一致的
domain和path,否则无法找到目标 Cookie,导致删除失败。
# 3. 读取
JavaScript 只能读取非 HttpOnly 的 Cookie。
const allCookies = document.cookie; // 返回一个字符串,格式如 "key1=value1; key2=value2"
- 可见性限制:设置了
HttpOnly标志的 Cookie 对客户端 JavaScript 脚本是不可见的,无法通过document.cookie读取或修改。
# 二、Cookie 操作的核心判断
一个 Cookie 能否被读取或写入,取决于以下几个核心条件的组合。
| 属性 | 限制 | 说明 |
|---|---|---|
HttpOnly | 无法通过 JS 读/写 | 该 Cookie 只能由服务器设置,并随 HTTP 请求自动发送,客户端脚本无权访问。 |
Domain 不匹配 | 无法读/写 | 当前页面的域名必须匹配 Cookie 的 domain 属性。例如,a.com 的页面无法操作 domain=b.com 的 Cookie。 |
Path 不匹配 | 无法读/写 | 当前页面的路径必须匹配 Cookie 的 path 属性。例如,/login 页面无法操作 path=/admin 的 Cookie。 |
Secure + 非 HTTPS | 无法写入 | 如果 Cookie 设置了 Secure 标志,那么它只能在 HTTPS 协议下被创建和发送。在 HTTP 页面上无法写入 Secure Cookie。 |
- 一句话总结:要让 JS 能操作 Cookie,必须同时满足:非
HttpOnly+ 当前域名匹配domain+ 当前路径匹配path。
# 三、跨子域共享
Cookie 的 domain 属性决定了其在不同子域之间的共享范围。
domain 设置 | 可用范围 | 示例 |
|---|---|---|
domain=test.example.com | 仅在 test.example.com 及其子域下可用 | test.example.com ✅ 可用sub.test.example.com ✅ 可用eagle.example.com ❌ 不可用 |
domain=.example.com(注意前面的点) | 在 example.com 的所有一级子域之间共享 | test.example.com ✅ 可用eagle.example.com ✅ 可用www.example.com ✅ 可用 |
- 最佳实践:为了在多个子域(如
www.a.com和app.a.com)之间共享登录状态,应将domain设置为顶级域名,如domain=.a.com。
# 跨子域操作能力(读取/修改/新增)
- 前提:
Domain=.example.com且当前路径命中Path;非HttpOnly才能被 JS 读取/修改;Secure仅在 HTTPS 下可写。 - 读取:其他子域(如
eagle.example.com)可读取命中domain/path的非HttpOnlyCookie。 - 修改(覆盖):其他子域可通过相同
name + domain + path覆盖该 Cookie 的值,变更对所有子域生效。 - 新增:其他子域可新增
Domain=.example.com的 Cookie,使其对所有子域可见。
# 示例(跨子域共享)
- 在
test.example.com设置共享 Cookie:
document.cookie = "uid=1; domain=.example.com; path=/";
- 在
eagle.example.com读取:
console.log(document.cookie); // "uid=1"(非 HttpOnly 才能看到)
- 在
eagle.example.com修改(覆盖):
document.cookie = "uid=2; domain=.example.com; path=/"; // 覆盖 uid
- 在
eagle.example.com新增:
document.cookie = "theme=dark; domain=.example.com; path=/";
- 回到
test.example.com读取:
console.log(document.cookie); // "uid=2; theme=dark; ..."
# 四、跨页面影响
不同页面对 Cookie 的操作是否会互相影响,完全取决于这些 Cookie 的 domain 和 path 是否相同。
[!TIP] 核心结论:只有当
domain和path完全相同时,我们才说它们是“同一个 Cookie”。
# 场景一:操作“同一个” Cookie(domain 和 path 均相同)
- 修改:✅ 全局生效。在任一符合条件的页面修改,所有其他符合条件的页面都能看到新值。
- 删除:✅ 全局生效。在一个页面删除,所有相关页面都将无法访问该 Cookie。
# 场景二:操作“不同” Cookie(domain 或 path 不同,即使 key 相同)
- 修改:❌ 互不影响。修改其中一个,另一个同名但
domain/path不同的 Cookie 不会改变。 - 删除:❌ 无法删除。尝试删除一个
domain/path不匹配的 Cookie 会静默失败。 - 读取:❌ 互相隔离。页面只能读取其
domain和path范围内的 Cookie。
# 五、HttpOnly 专栏
HttpOnly 是一个专门为增强安全性而设计的 Cookie 属性。
| 操作/能力 | 是否允许 | 说明 |
|---|---|---|
| JavaScript 读取 | ❌ 不允许 | document.cookie 看不到它。 |
| JavaScript 修改/删除 | ❌ 不允许 | 任何客户端脚本操作都会被忽略。 |
| 浏览器自动发送 | ✅ 允许 | 在每次向匹配的服务器发送 HTTP 请求时,浏览器会自动携带此 Cookie。 |
| 跨子域共享 | ✅ 允许 | 只要 domain 属性设置正确(如 .example.com),HttpOnly Cookie 同样可以在子域间共享。 |
- 核心用途:防止 XSS (跨站脚本) 攻击。即使页面被注入了恶意脚本,该脚本也无法窃取到
HttpOnly的 Cookie(如session id)。
# 适用范围(补充)
HttpOnly的作用范围是“单个 Cookie(一个key=value)”,不是某个域名或整个浏览器的全局开关;每个 Cookie 是否带HttpOnly由服务端通过Set-Cookie单独决定。- JS 仅能读取/修改非
HttpOnly的 Cookie,无法通过document.cookie创建或修改带HttpOnly的 Cookie。 - 浏览器请求会自动携带所有命中
domain/path的 Cookie,包括HttpOnly和非HttpOnly;因此接口请求能看到全部匹配的 Cookie,而 JS 只能看到其中非HttpOnly的部分。 - 是否添加或移除
HttpOnly只能通过服务端Set-Cookie完成;前端代码无法为已有 Cookie 增减HttpOnly属性。
# 示例(HttpOnly)
- 服务端设置(决定是否
HttpOnly):
Set-Cookie: sessionid=abc123; Path=/; Domain=.example.com; HttpOnly; Secure; SameSite=Lax
- JS 读取对比(只能看到非
HttpOnly):
document.cookie = "a=1; path=/; domain=.example.com";
console.log(document.cookie); // "a=1"(看不到 HttpOnly 的 sessionid)
- 接口请求会自动携带全部命中的 Cookie(含
HttpOnly):
fetch("https://test.example.com/api/me", { credentials: "include" });
// 请求头将自动包含:Cookie: a=1; sessionid=abc123(命中 domain/path 即携带)
- 删除 / 登出流程(前端无法变更
HttpOnly):
// 前端试图删除 HttpOnly:
document.cookie = "sessionid=; Max-Age=0; path=/; domain=.example.com"; // ❌ 无法删除原 HttpOnly 的 sessionid
// 正确做法:后端回包删除
Set-Cookie: sessionid=; Max-Age=0; Path=/; Domain=.example.com; HttpOnly
- 结论:
HttpOnly只能由服务端通过Set-Cookie控制;JS 只能读改非HttpOnly,请求会携带所有命中的 Cookie(包含HttpOnly)。
# 六、常见问题与陷阱
# 1. “修改”失败,实为“新建”
当尝试修改一个 Cookie,但提供的 domain 或 path 与原 Cookie 不符时,浏览器不会修改,而是会创建一个新的、同名的 Cookie。
示例:
- 已有 Cookie:
key=1; domain=.example.com; path=/admin - 你的代码:
document.cookie = "key=2; path=/" - 最终结果:浏览器中会同时存在两个名为
key的 Cookie:key=1; domain=.example.com; path=/adminkey=2; domain=.example.com; path=/(因为未指定 domain,默认使用当前页面的 domain)
# 2. 删除失败
最常见的原因是删除时指定的 domain 或 path 与原 Cookie 不一致。请务必检查浏览器开发者工具中 Cookie 的确切 domain 和 path 值。
# 3. 同名 Cookie 导致解析混乱
如果因为路径或域名的差异,导致浏览器中存在多个同名 Cookie,浏览器会将它们全部发送到服务器(在 Cookie 请求头中以分号分隔)。服务端应用程序在解析这个字符串时,其行为可能是不确定的,它可能会取第一个、最后一个,或导致解析错误,从而引发难以排查的 BUG。
# 七、本质总结
[!IMPORTANT] 一个 Cookie 是否可操作(读/写)、是否跨域、是否会互相影响,归根结底只取决于三个核心属性:
HttpOnly、Domain和Path。
Secure属性仅作为写入前置条件,不影响已存在 Cookie 的读取和作用范围。
- 本文链接: https://mrgaogang.github.io/javascript/base/Cookie%E7%9A%84%E4%BD%BF%E7%94%A8.html
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 许可协议。转载请注明出处!