首页
/ Puppeteer CookieSameSite 类型全解析:Strict / Lax / None / Default 的语义、源码映射与实战用法

Puppeteer CookieSameSite 类型全解析:Strict / Lax / None / Default 的语义、源码映射与实战用法

2026-09-08 17:54:55作者:齐冠琰

Puppeteer(本仓库:puppeteer1/puppeteer,提供 Chrome 与 Firefox 的 JavaScript API)使用 CookieSameSite 联合类型统一描述 Cookie 的 SameSite 状态。本篇以 docs/api/puppeteer.cookiesamesite_2.md 为主线,结合 puppeteer-core 中 CDP 与 WebDriver BiDi 两套协议适配层的源码实现,讲解四个取值 'Strict' | 'Lax' | 'None' | 'Default' 的真实语义、在 setCookie/cookies 各 API 中的使用位置,以及跨协议时发生的底层映射,帮助你写出可正确落盘、可跨浏览器验证的 Cookie 操作代码。

一、类型定义:文档说了什么

CookieSameSite 在 API 文档中以“Type alias”(类型别名)形式定义,说明文字为:

Represents the cookie's 'SameSite' status.

即:它表示 Cookie 的 SameSite 状态,并且文档明确将规范来源指向 https://tools.ietf.org/html/draft-west-first-party-cookies(First-Party Cookies 草案,也就是我们常说的 SameSite Cookie 规范草案)。其完整签名如下:

export type CookieSameSite = 'Strict' | 'Lax' | 'None' | 'Default';

这是一个纯字面量联合类型(string literal union),而不是枚举(enum)。也就是说在 TypeScript 中,只有当字段被显式声明为 CookieSameSite 时,这四个字符串字面量才可赋值;你无法通过索引枚举遍历它。

在类型汇总文档 docs/api/index.md 中,它被收录在 Type Aliases 列表(锚点 #cookiesamesite_2),并链接到本文档。而实际源码定义位于 packages/puppeteer-core/src/common/Cookie.ts

/**
 * Represents the cookie's 'SameSite' status:
 * https://tools.ietf.org/html/draft-west-first-party-cookies
 *
 * @public
 */
export type CookieSameSite = 'Strict' | 'Lax' | 'None' | 'Default';

文件为何命名为 cookiesamesite_2

细心的读者会发现 API 文档目录中该文件名为 puppeteer.cookiesamesite_2.md。从文档生成工具 docgen 的命名规律与类型汇总索引 #cookiesamesite_2 的锚点可以推断:文档生成器为同名标识符去重时添加了 _2 后缀(历史版本中曾存在一个同名 CookieSameSite 枚举)。因此在实际代码中引用的是这一份 Type alias,使用 _2 仅仅是文档产物层面的命名差异,不影响 API 使用。

二、四个取值逐个拆解

取值 语义要点 说明
'Strict' 严格模式 Cookie 只在“第一方上下文”中随请求发送;任何跨站请求都不会携带该 Cookie。安全性最高,但对跨站跳转后的首次访问(如从邮件/外链进入)不友好,常导致登录态在跳转落地页暂时丢失。
'Lax' 宽松模式 现代浏览器事实上的默认行为:顶层导航发起的跨站 GET 请求(如用户点击链接跳转)会携带 Cookie,而跨站的图片、脚本、XHR/fetch、iframe 等子资源请求不携带。在“安全”与“可用”之间取得平衡。
'None' 显式允许跨站携带 Cookie 在跨站上下文中也可以被发送。需要与 Secure 属性配合(见下文第六节),否则主流浏览器会拒绝。对应 Chrome 为支持第三方场景(如 SSO、支付回调)开放的选项。
'Default' 交给浏览器自行决定 在 Puppeteer 的类型层面占位,用于“不强制指定 SameSite”的意图。当它被传给 Chrome/CDP 时会被转换成“不发送该字段”(详见第四节),浏览器按自身的默认策略处理(现代 Chrome 将无 SameSite 属性的 Cookie 视同 Lax 处理)。

需要特别留意:四个取值是字符串字面量,首字母大写、其余小写,例如必须是 'None' 而不是 'none'。这一大小写约定贯穿 CookieParamCookieData 以及 page.cookies() 的返回值。而下文会看到,在 Firefox 走 WebDriver BiDi 时,协议内层使用小写 'none',由 Puppeteer 适配层负责转换,对使用方无感。

三、类型用在哪里:从 CookieParam 到 CookieData

CookieSameSite 是 Puppeteer Cookie 体系的基础类型之一,出现在“写入参数”与“读取结果”两侧,均在 packages/puppeteer-core/src/common/Cookie.ts 中定义:

  1. CookieParam.sameSite(页面级 API 写入参数)CookieParampage-level cookies API 使用的参数对象(文档见 puppeteer.cookieparam.md),其中字段声明为 sameSite?: CookieSameSite。它用于 Page.setCookie() / Page.deleteCookie() 这类以页面为作用域的调用。

  2. CookieData.sameSite(浏览器级 API 写入/读取参数)CookieDatabrowser-level cookies API 使用的参数对象(文档见 puppeteer.cookiedata.md),同样声明 sameSite?: CookieSameSite。它用于 browser.setCookie() / browser.cookies() / browser.deleteCookie() / BrowserContext.setCookie() / browserContext.cookies() 等以整个浏览器/浏览器上下文为作用域的调用。

  3. Cookie 接口(读取结果)puppeteer-core 内部还有 Cookie 接口(继承 CookieData,补充 pathexpiressizesecuresession 等只读字段)。因此通过 page.cookies() 拿到的每个 Cookie 对象上,也会带一个 sameSite: CookieSameSite 属性,供你校验设置是否生效、或分析站点当前的 Cookie 策略。

注意页面级与浏览器级两套 API 面向的对象不同:页面级 API 通常要求提供 url 或结合当前页面 URL 推断 domain/path,而浏览器级 API 需要显式提供 domain 等完整定位信息。二者对 sameSite 的处理完全一致,都收束到同一个类型。

四、底层原理(一):Chrome/CDP 路径下的真实映射

当 Puppeteer 连接 Chrome(通过 Chrome DevTools Protocol,即 CDP)时,sameSite 并不会被“原样透传”,而是经过一次转换。转换函数定义在 packages/puppeteer-core/src/cdp/Page.ts

/**
 * @internal
 */
export function convertSameSiteFromPuppeteerToCdp(
  sameSite: CookieSameSite | undefined,
): Protocol.Network.CookieSameSite | undefined {
  switch (sameSite) {
    case 'Strict':
    case 'Lax':
    case 'None':
      return sameSite;
    default:
      return undefined;
  }
}

从中可以读出三条关键事实:

  • 'Strict''Lax''None' 三个值与 CDP 协议层 Protocol.Network.CookieSameSite 中的字面量一一对应、直接透传,无需改写;
  • 'Default'(以及 undefined)会落到 default 分支,返回 undefined,即:该字段在发送给 CDP 时被省略,浏览器按自身默认 SameSite 策略处理;
  • 该函数被 CdpPage 在写入 Cookie 的调用链(如 page.setCookie() 处理 CookieParam、以及 cdp 层 BrowserContext.setCookie())中调用,例如 packages/puppeteer-core/src/cdp/Page.ts 处将 cookieParam.sameSite 转换后放入协议参数。

这也解释了 'Default' 的设计意图:它不是 CDP 协议里的真实取值(协议里还存在独立的 'Unspecified' 状态,表示“未指定”),而是 Puppeteer 面向用户提供的一个“语义化缺省值”。

五、底层原理(二):Firefox/WebDriver BiDi 路径下的双向转换

当 Puppeteer 连接 Firefox(或启用了 WebDriver BiDi 的 Chrome)时,Cookie 通过 WebDriver BiDi 的 storage 域读写。BiDi 协议内部对 SameSite 采用小写枚举(strict / lax / none),且带有一个 default 状态,与 Puppeteer 的大写字符串不同。因此 puppeteer-corepackages/puppeteer-core/src/bidi/Page.ts 中实现了两个方向的对偶转换函数:

读取方向(BiDi → Puppeteer),见 packages/puppeteer-core/src/bidi/Page.ts

function convertCookiesSameSiteBiDiToCdp(
  sameSite: Bidi.Network.SameSite | undefined,
): CookieSameSite {
  switch (sameSite) {
    case 'strict':
      return 'Strict';
    case 'lax':
      return 'Lax';
    case 'none':
      return 'None';
    default:
      return 'Default';
  }
}

写入方向(Puppeteer → BiDi),见 packages/puppeteer-core/src/bidi/Page.ts

export function convertCookiesSameSiteCdpToBiDi(
  sameSite: CookieSameSite | undefined,
): Bidi.Network.SameSite {
  switch (sameSite) {
    case 'Strict':
      return Bidi.Network.SameSite.Strict;
    case 'Lax':
      return Bidi.Network.SameSite.Lax;
    case 'None':
      return Bidi.Network.SameSite.None;
    default:
      return Bidi.Network.SameSite.Default;
  }
}

由此可见两条非常重要的设计结论:

  1. “Default”在 BiDi 侧是真实状态:读取一个未显式声明 SameSite 的 Cookie 时,BiDi 返回 default,Puppeteer 会把它归一化为字符串 'Default' 暴露给你——这是它与 CDP 路径的关键差异(CDP 下未指定的 Cookie 通常表现为没有该字段或 'Unspecified')。因此用 page.cookies() 在 Firefox 与 Chrome 上拿到同样的 sameSite: 'Default' 或缺失,属于协议差异,不代表 Cookie 本身不同。
  2. 该转换函数是 export 的,因为它还被 bidi/BrowserContext.ts 等模块复用(如 packages/puppeteer-core/src/bidi/BrowserContext.ts 处写入 Cookie 时同样先做 convertCookiesSameSiteCdpToBiDi 转换)。

六、实战:setCookie 中如何写、cookies 中如何验

1. 页面级设置 Cookie(CDP 路径)

使用 Page.setCookie()(API 见 puppeteer.page.setcookie.md),在 CookieParam 中传入 sameSite

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();

await page.setCookie(
  {
    name: 'pref',
    value: 'dark-theme',
    domain: 'example.com',
    sameSite: 'Lax',       // CookieSameSite 取值
  },
  {
    name: 'session_key',
    value: 'abc123',
    url: 'https://example.com/login',
    httpOnly: true,
    secure: true,
    sameSite: 'Strict',    // 严格模式
  },
);

2. 跨站场景设置 None 必须配合 Secure

要让 Cookie 允许跨站携带(如 SSO 回调、第三方登录态),应使用 sameSite: 'None',同时务必带上 secure: true 并基于 HTTPS 访问——因为现代浏览器会拒绝未带 Secure 属性的 SameSite=None Cookie。仓库中的集成测试印证了这一行为:在 test/src/cookies.test.ts 的用例 “should set secure same-site cookies from a frame” 中,测试用 httpsServer 启动页面,在跨进程 iframe 场景下通过 page.setCookie({..., sameSite: 'None'}) 写入,随后断言读取结果中 sameSite: 'None'secure: true。也就是说,即使代码里没有显式写 secure: true,Chrome 也会把 SameSite=None 的 Cookie 强制按 Secure 处理;反过来,在 http 页面里尝试写 sameSite: 'None' 大概率会被浏览器拒绝。

3. 读取校验(Firefox 与 Chrome 通用)

无论走哪条协议路径,读取 API 都返回统一大写形式的 CookieSameSite

const cookies = await page.cookies('https://example.com');
for (const c of cookies) {
  console.log(c.name, c.sameSite); // 例如 'Lax' / 'Strict' / 'None' / 'Default'
}

利用返回值可以快速断言“Cookie 是否真的按预期策略落盘”,例如校验三方登录态确实是 None

const sso = (await page.cookies('https://idp.example.com')).find(
  c => c.name === 'sso_session',
);
if (sso?.sameSite !== 'None') {
  throw new Error(`sso_session SameSite 异常: ${sso?.sameSite}`);
}

4. 浏览器级 API 同样支持

如果需要在创建 BrowserContext 之后、尚未导航任何页面之前就预置 Cookie,可用浏览器级 API browser.setCookie()browserContext.setCookie()(API 见 puppeteer.browser.setcookie.mdpuppeteer.browsercontext.setcookie.md),它们使用 CookieDatasameSite 字段类型完全一致:

const context = await browser.createBrowserContext();
await context.setCookie({
  name: 'geoloc-consent',
  value: '1',
  domain: 'example.com',
  secure: true,
  sameSite: 'Lax',
});
const page = await context.newPage();
await page.goto('https://example.com');

注意浏览器级 API 需要提供完整的 domain(甚至 path)来唯一定位 Cookie;若只想按当前页面域名收窄操作,页面级 API(提供 url)会更方便。

七、选择建议与注意事项小结

  1. 绝大多数内部自动化/无痕场景用 'Lax' 或直接省略即可:省略(或显式 'Default')时,CDP 路径不发送该字段,浏览器按默认策略处理;BiDi 路径读取时会归一化为 'Default'
  2. 登录态加固可选 'Strict':适合同站应用、期望最高防护等级、且能接受外链落地页暂不带登录态的场景。
  3. 只有确需跨站携带时才用 'None',且必须走 HTTPS + Secure,例如第三方登录(SSO)、支付回调、跨子域数据上报。
  4. 字符串大小写敏感:传 'NONE''none''Lax '(带空格)都无法通过 TypeScript 类型检查或会被底层当作非法值处理;写入用 'Strict' | 'Lax' | 'None' | 'Default',读取返回同一组值。
  5. 跨浏览器断言时留意协议差异:Chrome 与 Firefox 在读写同一枚“未声明 SameSite”的 Cookie 时,字段呈现方式可能不同(CDP 下可能缺省,BiDi 下返回 Default),Puppeteer 通过第四节/第五节的转换层在应用层尽量抹平差异,但断言时仍建议同时检查字段是否存在两种分支。

深入阅读指引:类型定义与 JSDoc 见 packages/puppeteer-core/src/common/Cookie.ts;CDP 映射逻辑见 packages/puppeteer-core/src/cdp/Page.ts;WebDriver BiDi 双向映射见 packages/puppeteer-core/src/bidi/Page.ts;配套参数接口文档见 puppeteer.cookieparam.mdpuppeteer.cookiedata.md;端到端行为测试见 test/src/cookies.test.ts

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391