第一章 地基:理解 Cookie 的本质
一、为什么需要 Cookie?
1. HTTP 的天生缺陷:无状态
HTTP 协议在设计之初便遵循"无状态"(Stateless)原则。所谓无状态,指的是服务器在处理请求时,不会记录客户端的任何历史信息------它不记得你是谁,也不记得你曾做过什么。每一次 HTTP 请求都是独立且孤立的。
这在实际应用中会带来什么问题?设想以下场景:
- 你在电商网站将心仪的商品加入购物车,跳转至结算页面时,服务器却回应:"你是谁?你的购物车空空如也。"
- 你刚刚成功登录,刷新页面后却被要求重新输入账号密码。

对服务器而言,每一次请求都像一位陌生访客的"初次见面"。这一设计在早期仅用于展示静态文档的 Web 中并无大碍,但随着现代 Web 应用承载起登录态、购物车、用户偏好等持续性状态,无状态就成了必须跨越的首要障碍。
2. 状态保持的需求
为了打破这一僵局,让服务器能够"记住"客户端,亟需一种机制来在多次 HTTP 请求间维持上下文 。其核心思路很简单:服务器为每位客户端分发一个唯一标识,客户端在后续每次请求中主动出示该标识,服务器便能轻松识别。
这个标识的具体载体,便是 Cookie。
一句话总结:HTTP 的无状态特性,催生了 Cookie 作为状态保持的基石。
二、Cookie 是什么?
1. 定义
Cookie 是浏览器存储的一小段文本数据,由服务器通过 HTTP 响应头下发给浏览器,浏览器保存后,在后续向同一服务器发起请求时自动将其携带回去。
拆解来看,三个关键点值得注意:
- 浏览器存储:数据保存在客户端(浏览器),而非服务器端。
- 一小段文本:单个 Cookie 的大小通常限制在 4KB 左右,不适合存放大量数据。
- 每次请求自动携带 :这是 Cookie 区别于
localStorage等其他存储方案的核心特征------无需开发者手动拼接,浏览器会自动将其附加在请求头中发送。
2. 一个直观的类比
不妨将 Cookie 类比为超市的会员卡:
- 首次光顾,你办理了一张会员卡(服务器下发 Cookie)。
- 超市系统记录下卡号,同时你将实体卡带回家(浏览器存储 Cookie)。
- 此后每次购物,你只需出示会员卡(请求自动携带 Cookie),收银员便能迅速识别你的身份和积分信息。

三、Cookie 的工作流程
Cookie 的完整生命周期可归纳为三个核心环节:
1. 第一步:服务端下发
服务器若希望在客户端种下 Cookie,会在 HTTP 响应中包含一个 Set-Cookie 头:
http
HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; Path=/; HttpOnly
该响应头明确告知浏览器:请将键值对 sessionId=abc123 存储下来,作用于全站路径,并禁止 JavaScript 读取。
2. 第二步:浏览器存储
浏览器收到 Set-Cookie 指令后,会依据附带的属性(如 Path、Domain 等)将其存入内部的 Cookie 存储区,并与对应的域名和路径相关联。
3. 第三步:后续请求自动携带
此后,浏览器每次向该服务器发送请求时,只要 Cookie 的 Domain 和 Path 匹配,便会自动在请求头中附上该 Cookie:
http
GET /user/profile HTTP/1.1
Host: example.com
Cookie: sessionId=abc123
整个流程可简化为以下时序图:
动手验证 :打开浏览器开发者工具(F12),在 Network 面板中观察任意请求的 Response Headers 和 Request Headers,你就能亲眼看到
Set-Cookie与Cookie这两个关键字段。
以无痕模式访问 www.baidu.com 为例:

- 可见百度服务器下发了 6 条
Set-Cookie响应头。 - 注意此时请求头尚未携带 Cookie(首次访问)。
- 小技巧:在 Network 面板中点击"Cookies"子选项卡,可以直观地查看本次请求下发了哪些 Cookie,以及后续请求携带了哪些 Cookie(若无 Cookie 传输,该选项卡可能不会显示)。

- 此时浏览器已经存储了Cookie,往后的每次请求,只需要符合要求,Cookie便自动携带上。我们继续往下找,便可以找到一个请求标头带有Cookie的请求:

- 同时,我们还可以从 应用程序->存储->Cookie 面板中查看浏览器对某域已经存储了的Cookie:

四、Cookie 的组成结构
一个完整的 Cookie 由两部分构成:键值对(name=value) 和 属性(attributes)。
1. 键值对(name=value)
这是 Cookie 的核心数据部分,格式为 名称=值。仍以百度首页响应中的 Set-Cookie 为例:

- 名称与值均为字符串。
- 若值中包含分号、逗号或空格等特殊字符,则需要进行 URL 编码。
2. 属性(元数据)
属性本身不参与数据存储,却决定了 Cookie 的作用边界 与安全策略。常见的属性列表如下:
| 属性 | 作用 | 示例 |
|---|---|---|
| Domain | 指定 Cookie 在哪些域名下生效 | Domain=example.com |
| Path | 指定 Cookie 在哪些路径下生效 | Path=/app |
| Expires | 设置过期时间(具体日期) | Expires=Wed, 21 Oct 2026 07:28:00 GMT |
| Max-Age | 设置有效时长(秒,相对时间) | Max-Age=3600 |
| Secure | 标记后仅通过 HTTPS 连接传输 | Secure |
| HttpOnly | 标记后禁止 JavaScript 读取 | HttpOnly |
| SameSite | 控制跨站请求是否携带 Cookie | SameSite=Lax |
此外,现代浏览器的 Cookies 面板中通常还会展示
分区键(Partition Key)、Cross Site(跨站标识)和优先级(Priority)等字段,便于开发者全方位诊断 Cookie 的作用域与行为细节。 这些属性在浏览器的Cookie调试面板中均可见:
一个完整的 Set-Cookie 示例:
http
Set-Cookie: sessionId=abc123; Domain=example.com; Path=/; Max-Age=86400; HttpOnly; Secure; SameSite=Lax
逐项解读:
- 键值对 :
sessionId=abc123 - Domain :作用于
example.com及其子域 - Path :作用于全站
/ - Max-Age:有效期为 86400 秒(即 24 小时)
- HttpOnly:禁止 JavaScript 读取
- Secure:仅通过 HTTPS 传输
- SameSite :设为
Lax(大多数跨站场景下不携带)
百度真实 Set-Cookie 示例:
http
Set-Cookie: BAIDUID=75EA0B9428BCEF18173F16D3E76A6B2A:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
逐项解读:
- 键值对 :
BAIDUID=75EA0B9428BCEF18173F16D3E76A6B2A:FG=1 - Domain :
.baidu.com(涵盖baidu.com及其所有子域) - Path :全站
/ - Max-Age / Expires :
max-age=2147483647秒(约 68 年),配合久远的expires,相当于持久化存储 - Secure:未设置,因此非 HTTPS 连接也可传输
- HttpOnly :未设置,JavaScript 可通过
document.cookie读取 - SameSite :未显式指定,现代浏览器通常采用默认值
Lax
五、同源策略、同站与跨站------Cookie 携带的根基
要透彻理解 Cookie 的携带规则,必须先厘清三个基础概念:同源(Same-Origin) 、同站(Same-Site) 与 跨站(Cross-Site)。许多关于 Cookie 的疑惑与误区,根源往往出在对这三者的混淆。
1. 同源(Same-Origin)
定义 :两个 URL 的协议(Scheme)、主机名(Host)和端口号(Port) 三者完全一致,方为同源。
判断示例:
| URL A | URL B | 是否同源 | 原因 |
|---|---|---|---|
http://a.com/ |
http://a.com/user |
✅ 同源 | 仅路径不同 |
http://a.com:3000 |
http://a.com:8080 |
❌ 不同源 | 端口不同 |
http://a.com |
https://a.com |
❌ 不同源 | 协议不同 |
http://a.com |
http://b.com |
❌ 不同源 | 域名不同 |
http://a.com |
http://sub.a.com |
❌ 不同源 | 子域名不同(被视作独立主机) |
2. 同站(Same-Site)
定义 :若两个 URL 的 eTLD+1(有效顶级域名 + 一级域名)相同,则视为同站。端口和协议不影响同站判断。
eTLD 是 "effective Top-Level Domain" 的缩写,由 Public Suffix List 维护。简言之,
.com、.org属于 TLD,而.com.cn、.github.io整体视作一个 eTLD。
判断示例:
| URL A | URL B | 是否同站 |
|---|---|---|
http://a.com |
https://a.com |
✅ 同站(协议无关) |
http://a.com:3000 |
http://a.com:8080 |
✅ 同站(端口无关) |
http://sub.a.com |
http://a.com |
✅ 同站(eTLD+1 均为 a.com) |
http://a.com |
http://b.com |
❌ 跨站 |
http://a.github.io |
http://b.github.io |
❌ 跨站(eTLD 为 github.io) |
3. 三者关系
三者的范围可概括为:
跨站 ⊃ 同站 ⊃ 同源
换言之:同源一定同站,但同站未必同源(端口或协议不同时)。而跨站则意味着 eTLD+1 都不相同。
4. 这对 Cookie 意味着什么?
Cookie 的携带规则受 Domain、Path 和 SameSite 属性的综合约束:
Domain与Path定义了 Cookie 的"作用范围"------哪些 URL 有权使用它。SameSite则决定了在"跨站"场景下,是否允许浏览器发送该 Cookie。
关键规则速查:
| SameSite 值 | 同站请求 | 跨站请求 |
|---|---|---|
Strict |
✅ 携带 | ❌ 不携带 |
Lax(默认) |
✅ 携带 | ⚠️ 部分携带(仅限顶级导航 GET 请求) |
None |
✅ 携带 | ✅ 携带(必须同时设置 Secure) |
这段规则的现实意义非常直接 :若你的前后端部署在不同域名下(跨站),即使 Domain 设置正确,只要 SameSite=Lax 或 Strict,Cookie 便不会被发送。唯有显式设置为 SameSite=None; Secure 方可跨站携带------而这又强制要求使用 HTTPS。
