Set-Cookie 只出现在响应里,一个首部行只装一个 cookie,要发三个就写三行。
它靠 Expires/Max-Age 定寿命,靠 Secure/HttpOnly 定边界。
实体首部讲完了。
这一节和下一节是 Cookie 的两半:服务端用它种,客户端用 Cookie 首部回。
一、为什么它不能合并成一行
别的首部同名可以合:Accept: a 加 Accept: b 等价于 Accept: a, b。
Set-Cookie 不行——它的值里本身就允许出现逗号(Expires 的日期就带逗号),一旦合并,
接收方没法可靠地再切开。
所以规矩是:一个 cookie 一行。
Demo 里种四个 cookie,响应里就有四行,整条响应 320 字节,其中大半是这四行。
这也带来一个副作用:cookie 多了,首部会显著变长。
HTTP/2 用 HPACK 压缩首部,能缓解一部分,但压不动随机串。
二、寿命的三种写法
Max-Age=3600 是相对秒数,从现在起算一小时。Expires 是绝对时刻,格式是 HTTP 日期。
两个都写时 Max-Age 优先,因为不怕时钟不对。
什么都不写,就是会话 cookie:存在内存里,关掉浏览器就没了。
Demo 里 sid 那条只写了 Path 和 HttpOnly,就是会话级的。
Max-Age=0 表示立刻删掉这一条。Demo 里 tmp=x 那条就是用来清场的。
反过来 Max-Age=-1 在有些实现里也按删除处理,但不如写 0 明确。
三、两个安全属性
Secure 限定只在 HTTPS 连接上发送。注意它不加密 cookie,只是不让它在明文连接里出现。
HttpOnly 限定脚本读不到(document.cookie 里看不到它)。这是防 XSS 偷 cookie 的主要手段之一。
这两个属性本机都没法验证行为——没有浏览器,也没有 HTTPS 服务端。
Demo 只验证了「首部确实发出去了、属性确实解析得出来」,行为层面这里不编结论。
真正要确认,得上浏览器加真证书试。
四、动手 Demo:一次种四个,看属性怎么拆
服务端在同一个响应里发四行 Set-Cookie,分别带 HttpOnly、Max-Age、Expires 加 Secure、
以及 Max-Age=0;客户端逐条拆出名字、值、属性和寿命。
return X.reply(u'HTTP/1.1 200 OK', [
(u'Set-Cookie', u'sid=abc123; Path=/; HttpOnly'),
(u'Set-Cookie', u'theme=dark; Path=/; Max-Age=3600'),
(u'Set-Cookie', u'seen=1; Expires=%s; Secure' % exp),
(u'Set-Cookie', u'tmp=x; Max-Age=0'),
], TEXT)
实跑输出:
--------------------------------------------------------------------
Set-Cookie:一次能发好几条,每条只装一个
--------------------------------------------------------------------
响应里一共 4 行 Set-Cookie,整条响应 320 字节:
Set-Cookie: sid=abc123; Path=/; HttpOnly
Set-Cookie: theme=dark; Path=/; Max-Age=3600
Set-Cookie: seen=1; Expires=Mon, 26 Oct 2026 06:44:58 GMT; Secure
Set-Cookie: tmp=x; Max-Age=0
客户端逐条拆开,属性各归各位:
名字 值 Path 有效期与标志
sid abc123 / HttpOnly
theme dark / Max-Age=3600(相对秒数)
seen 1 (无) Expires(绝对时刻)、Secure
tmp x (无) Max-Age=0(立即删)
四行里藏着四条规矩:
一、一个 Set-Cookie 只能装一个 cookie。四个 cookie 就要四行,
不能像别的首部那样用逗号合并——因为值里本身就可能有逗号。
二、`Max-Age` 是相对秒数,`Expires` 是绝对时刻,两个都写时 Max-Age 优先。
三、`Max-Age=0` 是「立刻删掉这一条」,可不是「永不过期」。
四、没有任何有效期属性的,是会话 cookie,关掉浏览器就没了。
至于 `HttpOnly` 到底能不能挡住脚本读取——本机没有浏览器,
这一条只验证了首部确实发出去了,行为层面测不到,这里不编结论。
四行全在,整条响应 320 字节。四个 cookie 就是四行,没有合并。
拆开之后:sid 只有 HttpOnly,是会话 cookie;theme 有 Max-Age=3600,相对一秒数;
seen 有 Expires 和 Secure;tmp 的 Max-Age=0 是删除指令。
第三行的 Expires 是个绝对时刻,写法里带逗号——这正是「不能把多行 Set-Cookie 合并」的原因。
四行属性加起来才把「这个 cookie 是谁的、活多久、能不能被脚本读」说全。
少写一个 Path,它的作用域就默认成当前目录,很容易埋坑。
30 秒实验
- 把
Max-Age=0改成Max-Age=-1,看解析出来的值是什么。 - 删掉
sid那行的HttpOnly,比一比响应少多少字节。 - 想一想:为什么
Secure不能代替 HTTPS?
常见误区
- 「多个 Set-Cookie 可以用逗号合并」——不行,值里本身可能有逗号。
- 「
Max-Age=0是永不过期」——正好相反,是立刻删除。 - 「
Secure会加密 cookie」——只是限定在加密连接上发送。
自测三问(点开对答案)
1. 要种三个 cookie,响应里要写几行 `Set-Cookie`? —— 三行,一个 cookie 一行,不能合并。 2. `Max-Age` 和 `Expires` 都写时听谁的? —— 听 `Max-Age`,它是相对秒数,不怕时钟不对。 3. 什么都不写寿命属性的 cookie 会怎样? —— 成为会话 cookie,存内存里,关掉浏览器就没了。服务端种下去,客户端还得回送。下一个字段就是回送时用的:Cookie。
- ☐ (本节完,下一节 6.7.2 Cookie)