Cookie 是客户端回送的那一半:所有 cookie 挤在一行里,用分号加空格分隔。
它按 Path/Domain 过滤,每发一个请求就带一次——这也是它吃带宽的原因。
上一节是种,这一节是收。两个字段合起来才构成「HTTP 无状态,但能记住你是谁」。
一、回送时是一行
服务端可以发四行 Set-Cookie,客户端回送时却只有一行 Cookie,值之间用 ; 分隔:
sid=abc123; theme=dark; lang=zh-CN。
这是很多人第一次抓包时的困惑:种下去是四行,收回来是一行。
原因很简单——回送只需要「名字=值」,属性和作用域已经在客户端那边生效过了。
所以 Cookie 首部里不会出现 Path、Expires、Secure。只带键值对,别的一概不带。
二、发之前先按作用域筛
客户端不是把整个 jar 都塞进去,而是先按 Path 和 Domain 筛一遍。
Demo 里四条 cookie,其中 lang 的 Path=/docs。
三种路径的实测:请求 / 发三条,Cookie 那行 37 字节、请求共 74 字节;
请求 /docs/intro 发四条,Cookie 行 49 字节、请求 96 字节;请求 /admin 又回到三条。
差别全在 lang 身上。这条 12 字节的 lang=zh-CN 在 /docs 下面才出现,路径一换就消失。
作用是「别把无关的 cookie 到处送」。
三、带宽账要算清
cookie 是跟着每一个请求走的。一个页面引 60 个资源,就是 60 次带 cookie 的请求;
cookie 总共 500 字节,这一页就白扔 30 KB。
所以规范建议单个 cookie 不超过 4 KB,单个域名的总数不超过几十个。
静态资源放在独立域名下(不带 cookie),就是省这笔钱的老办法。
另一条路是 Domain 与 Path 尽量写窄:只给 /api 用的 cookie 就别设成 Path=/,
否则整站的每个请求都替它付费。
四、动手 Demo:四条 cookie,三种路径,看筛掉哪条
本地维护一个四条 cookie 的 jar,其中一条的 Path 是 /docs;
按三条不同路径各构造一次请求,量出 Cookie 那行和整个请求的字节数。
jar = [(u'sid', u'abc123', u'/'),
(u'theme', u'dark', u'/'),
(u'lang', u'zh-CN', u'/docs'),
(u'tmp', u'x', u'/')]
def build(path):
out = []
for name, val, cpath in jar:
if path.startswith(cpath): # Path 前缀匹配
out.append(u'%s=%s' % (name, val))
return u'; '.join(out) # 一行,分号加空格
实跑输出:
--------------------------------------------------------------------
Cookie:回送时全挤在一行,用分号隔开
--------------------------------------------------------------------
客户端手上存了四条 cookie,作用域不同:
sid=abc123 Path=/
theme=dark Path=/
lang=zh-CN Path=/docs
tmp=x Path=/
请求 / Cookie: sid=abc123; theme=dark; tmp=x
请求共 74 字节,其中 Cookie 那行 37 字节
请求 /docs/intro Cookie: sid=abc123; theme=dark; lang=zh-CN; tmp=x
请求共 96 字节,其中 Cookie 那行 49 字节
请求 /admin Cookie: sid=abc123; theme=dark; tmp=x
请求共 79 字节,其中 Cookie 那行 37 字节
真发一次,看服务端收到什么:
发出去的:sid=abc123; theme=dark; lang=zh-CN; tmp=x
服务端回:收到 sid=abc123; theme=dark; lang=zh-CN; tmp=x
三件事:
一、所有 cookie 挤在**一行**里,用 `; ` 分隔,不是多行。
二、`Path=/docs` 那条只在 `/docs` 开头的路径上才发;请求 `/admin` 时它不在。
三、每请求都带,所以它吃的带宽是「每条 cookie 乘上请求数」——
这就是为什么规范建议单个 cookie 别超过 4 KB,总数别太多。
/ 和 /admin 都发了三条,Cookie 行 37 字节、请求 74 与 79 字节。
/admin 多那 5 字节是路径本身变长了。
/docs/intro 发四条,Cookie 行 49 字节、请求 96 字节。
多出来的 12 字节正是 lang=zh-CN 加分隔符。
服务端那一次真收到了 sid=abc123; theme=dark; lang=zh-CN; tmp=x,和客户端构造的字符串一字不差。
注意 Path 匹配是前缀匹配,而且按目录边界算:/docs 能匹配 /docs/intro,
但一般不该匹配 /docs2——这一层细节各实现略有差别,别把安全边界压在它上面。
30 秒实验
- 把
lang的Path改成/,看三个请求的 Cookie 行长度会不会都一样。 - 加一条
Path=/docs但名字相同的 cookie,看同名时哪个生效。 - 想一想:为什么登录态的 cookie 一般不设
Path=/?
常见误区
- 「Cookie 首部会带上 Path 和 Expires」——不带,只带键值对。
- 「可以用多行 Cookie」——回送只有一行,多条用分号分隔。
- 「cookie 只在需要时才发」——只要路径和域匹配,每个请求都发。
自测三问(点开对答案)
1. 客户端回送 cookie 时是一行还是多行? —— 一行,多个键值对用分号加空格分隔。 2. `Path=/docs` 的 cookie 会在请求 `/admin` 时发送吗? —— 不会,路径不匹配就被筛掉。 3. 为什么静态资源常放在不带 cookie 的域名下? —— cookie 跟着每个请求走,省掉它就是省带宽。Cookie 两节完了。最后四个字段都是「附加约定」,而且四个里有三个已经退场——先从还能用的那个说起。
- ☐ (本节完,下一节 6.8.1 X-Frame-Options)