Set-Cookie
用法
Set-Cookie 头部指示客户端存储一个名称-值对,以及控制 cookie 作用域、生命周期和安全属性的可选属性。每个 Set-Cookie 头部恰好设置一个 cookie。发送多个 cookie 的服务器会在同一响应中包含多个 Set-Cookie 头部。
一般语法为以等号分隔的 cookie 名称和值,后跟以分号分隔的可选属性。
Set-Cookie: <name>=<value>; <attributes>
cookie 名称接受除控制字符、空格、制表符以及分隔符字符 ( ) < > @ , ; : \ ” / [ ] ? = { } 之外的 US-ASCII 字符。cookie 值接受除控制字符、空白、双引号、逗号、分号和反斜杠之外的 US-ASCII 字符。值可以选择用双引号包裹。
浏览器会阻止前端 JavaScript 通过 Fetch API 和 XMLHttpRequest 读取 Set-Cookie 头部。在 Fetch 规范中,该头部是一个禁止的响应头名称。除非请求包含凭据,否则 CORS 响应中的 cookie 会被忽略。
Cookie prefixes
cookie 名称前缀会施加由浏览器强制执行的额外约束,从而对 cookie 的设置位置和方式提供保证。
__Secure- 前缀要求 Secure 属性和 HTTPS 源。这可以防止仅使用 HTTP 的站点设置与安全 cookie 同名的 cookie。
Set-Cookie: __Secure-ID=abc; Secure; Path=/
__Host- 前缀增加了更严格的要求:Secure 属性、无 Domain 属性,以及 Path 设为 /。这将 cookie 绑定到确切的源主机名,防止子域干扰。
Set-Cookie: __Host-ID=abc; Secure; Path=/
__Http- 前缀同时要求 Secure 和 HttpOnly 属性,且只能通过 Set-Cookie 头部设置,无法通过 JavaScript 设置。
Set-Cookie: __Http-ID=abc; Secure; HttpOnly
__Host-Http- 前缀结合了 __Host- 和 __Http- 的限制,构成可用的最严格的 cookie 绑定。
Set-Cookie: __Host-Http-ID=abc; Secure; HttpOnly; Path=/
注意
__Http- 和 __Host-Http- 前缀来自一个独立的 cookie 前缀提案,不同于浏览器已广泛强制执行的成熟的 __Secure- 和 __Host- 前缀。这些较新的前缀仍处于提案阶段。
SEO 影响
Googlebot 会在源设置 cookie 时发送 cookie,但不会在各次独立的抓取会话之间持久保存 cookie。资源域上过大的 cookie 会在渲染期间增加请求大小。为静态资源使用无 cookie 的域可避免这种开销。请避免将内容置于依赖 cookie 的逻辑之后,导致爬虫无法看到页面。
Expires
Expires 属性以 HTTP-date 格式设置一个绝对过期日期。在此日期之后,客户端不再发送该 cookie。当它与 Max-Age 一同被省略时,该 cookie 会成为会话 cookie,在客户端关闭时被移除。浏览器会使用响应中的 Date 头部来校正时钟偏差。
Expires=Thu, 31 Dec 2026 23:59:59 GMT
注意
浏览器会将 Expires 值上限限制为自 cookie 设置起 400 天。此限制允许大约每年访问一次的站点维持 cookie,同时防止无限期持久化。
Max-Age
Max-Age 属性以客户端收到响应起的秒数设置 cookie 生命周期。零或负值会立即使 cookie 过期。当 Expires 和 Max-Age 同时存在时,Max-Age 优先。
Max-Age=2592000
JavaScript 中的 Cookie Store API 也通过 cookieStore.set() 方法暴露了一个 maxAge 选项,为程序化的 cookie 管理镜像了该属性。该 API 不允许在同一个 cookie 上同时设置 expires 和 maxAge。
注意
浏览器会将 Max-Age 值上限限制为 400 天(34,560,000 秒),与 Expires 上限一致。
Domain
Domain 属性指定哪些主机会收到该 cookie。设置后,cookie 对指定的域及其所有子域可用。省略时,cookie 默认限定于确切的源主机,不包括子域(即仅限主机的 cookie)。只有当前域或当前主机的父域才有效。前导点会被忽略。
Domain=example.re
Path
Path 属性将 cookie 的传输限制在匹配指定 URL 路径前缀的请求上。正斜杠 / 用于分隔路径段。带有 Path=/docs 的 cookie 匹配 /docs、/docs/、/docs/Web/HTTP,但不匹配 / 或 /docsets。
Path=/
注意
Path 属性不是一种安全机制。同源上的任何脚本都能访问所有 cookie,无论路径限制如何。
Secure
Secure 属性将 cookie 限制在 HTTPS 连接上。浏览器绝不会通过未加密的 HTTP 发送安全 cookie。不安全的源无法设置带有此属性的 cookie。在开发期间,localhost 不受 HTTPS 要求的约束。
HttpOnly
HttpOnly 属性会阻止 JavaScript 通过 document.cookie、Cookie Store API 及其他 DOM 接口访问该 cookie。但由 JavaScript 通过 fetch() 和 XMLHttpRequest 发起的 HTTP 请求仍会发送该 cookie。此属性可缓解试图窃取 cookie 值的跨站脚本(XSS)攻击。
SameSite
SameSite 属性控制浏览器是否随跨站请求发送该 cookie。定义了三个值。
Strict 仅随同站请求发送 cookie。跨站导航(包括从外部站点点击链接)不会包含该 cookie。
Lax 随同站请求以及地址栏发生变化的跨站顶级导航发送 cookie。子资源请求、fetch() 调用和 iframe 导航不会包含该 cookie。当未指定 SameSite 属性时,大多数浏览器默认使用 Lax。
None 随所有请求发送 cookie,包括跨站请求。使用 SameSite=None 时必须带有 Secure 属性。来自不同协议(HTTP 与 HTTPS)的 cookie 会被视为跨站。
SameSite=Lax
Partitioned
Partitioned 属性将 cookie 存储在以 cookie 源和顶级站点共同作为键的分区存储中。由嵌入在 shop.example.org 上的 cdn.example.re 设置的分区 cookie,只有在 cdn.example.re 嵌入于 shop.example.org 时才会发送,嵌入于其他站点时则不会。
此属性是 Cookies Having Independent Partitioned State(CHIPS)提案的一部分。该属性支持嵌入式挂件、CDN 负载均衡和无头 CMS 提供商等合法的跨站 cookie 用例,同时不授予完整的跨站追踪能力。
必须带有 Secure 属性。推荐使用 __Host- 前缀将 cookie 绑定到确切的源。
Set-Cookie: __Host-sess=abc; SameSite=None; Secure; Path=/; Partitioned
基于 Chromium 的浏览器和 Firefox 支持此属性。Safari 默认通过一种独立机制对第三方 cookie 进行分区,不使用此属性。
示例
一个没有过期属性的会话 cookie。该 cookie 会在客户端会话结束时被移除。
Set-Cookie: sessionId=38afes7a8
一个生命周期为 30 天的持久化 cookie。Max-Age 属性设置相对于当前时间的过期时间。
Set-Cookie: id=a3fWa; Max-Age=2592000
一个带有适用于认证令牌的安全属性的 cookie。HttpOnly 属性阻止 JavaScript 访问,Secure 限制在 HTTPS 上,SameSite=Strict 防止跨站传输。
Set-Cookie: __Host-token=eyJhbGci; Secure; HttpOnly; SameSite=Strict; Path=/
一个用于跨站嵌入的分区第三方 cookie。Partitioned 属性将该 cookie 隔离到嵌入出现的特定顶级站点。
Set-Cookie: __Host-embed=34d8g; SameSite=None; Secure; Path=/; Partitioned
在单个响应中设置多个 cookie。每个 cookie 都需要各自的 Set-Cookie 头部。
Set-Cookie: theme=dark; Path=/; Max-Age=31536000
Set-Cookie: __Host-sid=x8Kp2; Secure; HttpOnly; SameSite=Lax; Path=/
疑难解答
未能持久化或到达错误请求上的 cookie,通常可追溯到属性不匹配或浏览器强制策略。
由于在 HTTP 上使用 Secure 标志导致 cookie 未被存储。Secure 属性要求 HTTPS 连接。通过普通 HTTP 设置的带 Secure 的 cookie 会被静默丢弃。请检查浏览器地址栏中的协议,并在 DevTools 的 Network 选项卡中检查 Set-Cookie 头部。在开发期间,localhost 不受此限制约束。
跨站请求不发送 cookie(SameSite 默认值)。当不存在 SameSite 属性时,现代浏览器默认使用 SameSite=Lax。诸如 fetch() 调用、iframe 和图像加载之类的跨站子请求不会包含该 cookie。当 cookie 必须跨站传输时,请添加 SameSite=None; Secure,或重新组织流程以避免跨站依赖。
cookie 路径不匹配。带有 Path=/app 的 cookie 不会发送到指向 /api 或 / 的请求。请确认 Path 值与需要该 cookie 的请求路径匹配。设置 Path=/ 可使 cookie 在整个站点范围内可用。
Domain 属性作用域问题。带有 Domain=sub.example.re 的 cookie 不会发送到 example.re。省略 Domain 属性会创建一个仅限于确切主机名的仅限主机的 cookie。值中的前导点会被浏览器忽略。请使用 DevTools 的 Application > Cookies 面板来检查每个已存储 cookie 的有效域。
第三方 cookie 被浏览器隐私功能阻止。Safari 默认通过 Intelligent Tracking Prevention 阻止第三方 cookie。Firefox 的 Enhanced Tracking Protection 和 Chrome 的 Privacy Sandbox 施加类似限制。Partitioned 属性(CHIPS)为合法的跨站 cookie 用例提供了一条基于标准的路径。请在多个浏览器中测试以确认行为。
cookie 超过最大大小。浏览器强制每个 cookie(名称、值和属性合计)约 4096 字节的限制。超大的 cookie 会被静默丢弃。请将大的负载移至服务端会话,仅在 cookie 中存储一个会话标识符。
调试已存储的 cookie。打开 DevTools > Application > Cookies 即可检查当前站点的所有 cookie,包括属性、过期时间和大小。document.cookie API 不会暴露 HttpOnly cookie。请使用 curl -v 查看服务器返回的原始 Set-Cookie 头部。