一、什么是 Cookie?
- 简单说:Cookie 是网站为了记住你,存放在你浏览器里的一小段文本信息,就像一张"临时身份证"或"便签条"。
- 工作原理:
-
- 访问网站时,服务器可以生成一个 Cookie 并发送给你的浏览器;
-
- 浏览器会把它保存下来;
-
- 下次再访问同一个网站时,浏览器会自动把这个 Cookie 带上,发给服务器;
-
- 服务器通过读取 Cookie 就知道"哦,又是你",从而提供个性化内容(比如保持登录状态)。
- 特点:
-
- 大小有限,一般每个 Cookie 不超过 4KB;
-
- 有有效期,可以是"会话级"(关闭浏览器就消失),也可以是"持久化"(比如 7 天后过期);
-
- 遵循同源策略(但有一些放宽机制,后面会讲到)。
二、Cookie 的结构与属性
- 一个 Cookie 由"键值对"和若干"元数据属性"组成,如下所示,HTTP 响应头中的 Set-Cookie:
swift
Set-Cookie: sessionId=abc123; Domain=.example.com; Path=/shop; Expires=Wed, 21 Oct 2026 07:28:00 GMT; Secure; HttpOnly; SameSite=Lax
- 必选部分:Name=Value:例如 sessionId=abc123,存储实际数据。
- 可选属性(控制行为):
| 属性 | 含义 | 结构 |
|---|---|---|
| Domain | 指定哪些主机可以接收该 Cookie。默认为当前域名(不含子域) | Domain=.example.com 允许所有 *.example.com 子域共享此 Cookie |
| Path | 指定 URL 路径下才发送 Cookie | Path=/shop 表示仅当请求 /shop 或其子路径时携带 |
| Expires / Max-Age | 控制有效期,Expires 指定绝对过期时间;Max-Age 指定相对秒数(现代优先),不设置则为会话级 Cookie(关闭浏览器即删除) | Max-Age=3600 表示 1 小时后失效 |
| Secure | 标记后,Cookie 仅能通过 HTTPS 协议传输 | 防止中间人攻击窃取 |
| HttpOnly | 标记后,无法通过 document.cookie 访问,只能由 HTTP 请求携带 | 防范 XSS 攻击盗取 Cookie |
| SameSite | 控制跨站请求是否发送 Cookie 值:Strict、Lax、None | SameSite=Lax(默认现代浏览器行为)允许通过顶级导航(如点击链接)发送,禁止 POST 表单跨站发送 |
| Priority | 低优先级 Cookie 可能被浏览器优先清除 | Priority=High |
三、Cookie 的作用域
- 浏览器发送 Cookie 的规则(必须同时满足):
-
- 域名匹配:如果 Domain 显式设置,则请求的域名必须与该 Domain(或其子域)完全匹配;如果未设置 Domain,则只对当前设置的域名(不含子域)发送。如:在 login.example.com 设置 Cookie 且未指定 Domain,则 api.example.com 不会收到该 Cookie。
-
- 路径匹配:请求的 URL 路径必须是 Cookie Path 或其子路径,例:Path=/app 的 Cookie,在请求 /app/user 时会发送,但请求 /admin 时不发送。
-
- 协议匹配(Secure 属性):带 Secure 的 Cookie 只在 HTTPS 请求中发送。
-
- SameSite 校验;
-
- 未过期(Expires / Max-Age)。
四、Cookie 的实际使用场景
① 用户登录(会话管理)
- 用户 POST 用户名密码 → 服务器验证成功 → 生成一个随机的 sessionId → 通过 Set-Cookie 下发(HttpOnly; Secure; SameSite=Lax);
- 后续请求浏览器自动携带该 Cookie → 服务器根据 sessionId 查到用户信息 → 保持登录状态。
② 记住"7 天免登录"
- 登录时勾选"记住我" → 服务器除了下发会话 Cookie,再设置一个 remember_token,Max-Age=604800(7天);
- 下次访问时,若会话过期但 remember_token 有效,服务器自动重建会话。
③ 记录用户偏好(如主题颜色)
- 用户点击"深色模式" → JS 执行 document.cookie = "theme=dark; max-age=31536000; path=/";
- 下次访问时,JS 读取 document.cookie 中的 theme 值,直接应用深色模式(无需请求服务器)。
④ 购物车(未登录状态)
- 用户添加商品时,前端将商品 ID 和数量存入 Cookie(如 cart=[{"id":1,"qty":2}] 编码后);
- 结账时,JS 读取 Cookie 中的购物车数据发给服务器;
- 缺点:Cookie 大小受限,只适合少量商品;登录后可迁移到服务端存储。
⑤ A/B 测试分组
- 用户首次访问时,服务器随机分配一个分组(如 group=A)并设置 Cookie,有效期 1 个月;
- 后续该用户始终看到 A 版本的界面,保证实验一致性。
五、Cookie 的生成、读取、修改、删除
- 服务器端生成(最常用),通过 HTTP 响应头 Set-Cookie 下发,Node.js (Express) 示例:
swift
res.cookie('username', 'Alice', { maxAge: 900000, httpOnly: true, secure: true });
- 客户端通过 JavaScript 生成(不常用,且受 HttpOnly 限制),设置的 Cookie 无法加 HttpOnly,且不能跨域:
swift
document.cookie = "color=blue; path=/; max-age=86400";
- 读取 Cookie:
-
- 服务端:通过 HTTP 请求头 Cookie 获取;
swift
Cookie: sessionId=abc123; color=blue
-
- 客户端 JS:console.log(document.cookie) 得到所有非 HttpOnly 的 Cookie,以分号分隔的字符串。
- 修改 Cookie:使用相同的 Name、Domain、Path 重新设置一次即可覆盖旧值:
swift
// 修改名为 color 的值
document.cookie = "color=red; path=/";
- 删除 Cookie,设置过期时间为过去的时间:
swift
document.cookie = "color=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/";
六、客户端可以修改 Cookie 吗?
- 客户端是可以修改 Cookie 的,因为 Cookie 就存储在用户自己的浏览器里,用户有完全的控制权。
- 可以通过浏览器开发者工具修改:
-
- 按 F12 打开开发者工具 → 选择 Application(或"存储")标签 → 左侧找到 Cookies → 选中当前网站;
-
- 你会看到所有 Cookie 的列表(名称、值、域名、路径等);
-
- 双击"值"那一列直接编辑,或者新增/删除。
- 通过 JavaScript(前提是 Cookie 没有设置 HttpOnly 属性):
swift
// 查看所有 cookie
console.log(document.cookie);
// 修改一个 cookie(直接覆盖)
document.cookie = "username=newValue; path=/";
// 添加新 cookie
document.cookie = "theme=dark; max-age=3600";
// 删除 cookie(设置过期时间为过去的时间)
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/";
- 如果服务器在设置 Cookie 时加了 HttpOnly 标记,那么 document.cookie 是读不到也改不了这个 Cookie 的(目的是防止 XSS 攻击)。但用户依然可以通过开发者工具直接修改它,因为那是浏览器提供的原生界面。
七、Cookie 可以实现跨域访问吗?
- 默认情况是不可行的:Cookie 遵循同源策略,即一个域(如 a.com)设置的 Cookie 只能被 a.com 的请求自动携带,不会发给 b.com,这是出于安全考虑。
- 但有限条件下可以,可以实现"跨域"访问:
-
- 同一父域下的子域:可以通过设置 Domain 属性实现"跨子域"共享。在 login.example.com 设置 Cookie 时,指定 Domain=.example.com(注意前面的点),那么 www.example.com、api.example.com 等所有子域都能访问到这个 Cookie,这不算严格的跨域,但经常被问成"跨域"。
-
-
- 服务器在设置 Cookie 时加上 SameSite=None 和 Secure(必须 HTTPS)。
-
-
-
- 前端发起请求时(比如用 fetch 或 XMLHttpRequest)加上 credentials: 'include' 选项。
-
-
- 这种方式有安全风险(CSRF),现代浏览器限制很严格,一般仅用于受信任的服务之间。
八、Cookie 的其他常见问题
- Cookie 中能不能存储中文? Cookie 可以存储中文,但最好进行 encodeURIComponent 编码,避免分号、逗号等特殊字符问题:
swift
document.cookie = `name=${encodeURIComponent('张三')}; path=/`;
- 如何让 Cookie 只在当前浏览器会话有效? 不设置 Expires 和 Max-Age 即可(会话级 Cookie)。
- 删除一个 Cookie 时为什么必须指定 Path 和 Domain? 因为 Cookie 是通过 Name + Domain + Path 三元组唯一标识的,如果不提供相同的 Domain 和 Path,浏览器会认为是在设置一个不同的 Cookie(而非删除目标 Cookie)。假设原 Cookie 有 Path=/app),可能删不掉,如下所示的错误示例:
swift
document.cookie = "color=; expires=..."
- 如何防止 Cookie 被 CSRF 读取(但 CSRF 不需要读取,只需要利用)? 设置 SameSite=Lax 或 Strict,Lax 允许从外部链接进入时携带,但禁止 POST 表单等跨站请求携带,能防御绝大部分 CSRF。
- Cookie 的 Max-Age 和 Expires 同时出现时哪个优先? 现代浏览器中 Max-Age 优先,如果两者都未设置,则为会话级。