Cookie 是 HTTP 里最古老、也最容易误用的状态机制:一个 Set-Cookie 头里能写多少属性,它们怎么决定一个 Cookie 在什么时候被保存、什么时候被带上?这篇看真实站点的 Set-Cookie,用 cookiejar 验证保存与发送的规则,最后用规则模拟表整理 SameSite 的三种取值。
收到 4 个 Set-Cookie 头(同名头可以出现多次,不能像别的头一样用逗号合并)
名字 BAIDUID 值 E9C938…(37 字符);属性 {'expires': 'Thu, 31-Dec-37 23:55:55 GMT', 'max-age': '2147483647', 'path': '/', 'domain': '.baidu.com'}
名字 BIDUPSID 值 E9C938…(32 字符);属性 {'expires': 'Thu, 31-Dec-37 23:55:55 GMT', 'max-age': '2147483647', 'path': '/', 'domain': '.baidu.com'}
名字 PSTM 值 179206…(10 字符);属性 {'expires': 'Thu, 31-Dec-37 23:55:55 GMT', 'max-age': '2147483647', 'path': '/', 'domain': '.baidu.com'}
名字 BAIDUID 值 E9C938…(37 字符);属性 {'max-age': '31536000', 'expires': 'Fri, 15-Oct-27 10:28:49 GMT', 'domain': '.baidu.com', 'path': '/', 'version': '1', 'comment': 'bd'}
带 Max-Age 的:[](秒;2147483647 秒 ≈ 68 年)名字=值加一串属性;同名头可重复出现,不能逗号合并。
服务器下发了 8 个 Cookie(a~h);cookiejar 实际保存了 5 个:
a=1 domain=127.0.0.1 path=/ secure=False 过期=会话结束 仅HTTP=False
c=3 domain=127.0.0.1 path=/ secure=True 过期=会话结束 仅HTTP=False
d=4 domain=127.0.0.1 path=/ secure=False 过期=1792063729 仅HTTP=True
b=2 domain=127.0.0.1 path=/admin secure=False 过期=会话结束 仅HTTP=False
h=8 domain=127.0.0.1 path=/api secure=False 过期=会话结束 仅HTTP=False
没有保存的:['e', 'f', 'g'](e:Domain=example.org 与请求的主机不匹配,被拒绝;f:Max-Age=0 表示立刻删除;g:Expires 已过期)
请求 / → 带上的 Cookie 头:a=1; d=4
请求 /admin/x → 带上的 Cookie 头:b=2; a=1; d=4
请求 /api/x → 带上的 Cookie 头:h=8; a=1; d=4
c(Secure)在明文 HTTP 请求里没有被带上;b(Path=/admin)只在 /admin 下带上;h(Path=/api)在 /api 下带上;SameSite 是浏览器对“跨站请求”的限制,cookiejar 不实现,所以 h 的 SameSite=Strict 在这里没有起作用域名不匹配被拒绝,Max-Age=0 删除,已过期丢弃;请求带上哪些由 Path、Secure、Domain 决定,长路径排前。
Cookie 头值长 100 字节 → HTTP/1.1 200 OK Cookie 头值长 4000 字节 → HTTP/1.1 200 OK Cookie 头值长 8000 字节 → HTTP/1.1 200 OK Cookie 头值长 16000 字节 → HTTP/1.1 200 OK Cookie 头值长 32000 字节 → HTTP/1.1 200 OK Cookie 头值长 64000 字节 → HTTP/1.1 200 OK
example.com 到 64000 字节的 Cookie 头都接受。
情形 Strict Lax None(须 Secure)
同站点内的任何请求 带 带 带
在 b.com 页面里的 <img src=a.com/x> 不带 不带 带
在 b.com 页面里点链接跳到 a.com(顶层导航 GET) 不带 带 带
b.com 的表单 POST 到 a.com(顶层导航 POST) 不带 不带 带
b.com 的 fetch/XHR 请求 a.com 不带 不带 带
(表中“站点”指可注册域名 eTLD+1,example.com 与 api.example.com 算同一站点;这张表是我按规范写的,不是实测浏览器)Strict 只在同站点带;Lax 额外允许顶层导航的 GET;None 总是带但必须 Secure。表是规则模拟。