ego (lite) 只是一款浏览器;ego 才是你跨设备的个人 Agent。
加入候补名单
PlaywrightstorageStateCookieslocalStorage浏览器自动化ego (lite)

Playwright storageState:JSON 存了什么、怎么加载、如何校验是否生效

2026年9月18日11 分钟阅读
绿色 ego (lite) 吉祥物站在上锁的蓝色储物柜旁,搞不清快照放在哪一格

Playwright 的 storageState 文件看起来可以很完整,却仍还原不出应用真正依赖的状态。cookies 可能在,localStorage 可能在,JSON 也可能完全合法,但 sessionStorage 默认不在文件里。应用若把部分会话放在那里,把文件加载到新 context 也带不回来。

这个差别很重要,因为 storageState 是快照,不是完整浏览器配置文件。Playwright 让这份快照容易导出、加载、按账号隔离,也容易校验。但任务若依赖装不进这个文件的浏览器状态,例如 sessionStorage、扩展、已有的登录配置文件,或要人完成 MFA,ego (lite) 走另一条路:把真实 Chromium 环境留在原地,而不是试图用 JSON 重建。

本指南只谈快照本身:storageState 里有什么、怎么生成与加载、账号和环境怎么分开,以及如何证明还原真的生效。登录循环与自动重新认证是另一个问题。这里问的更单纯:这个文件真正存了什么,又留下了什么?

Playwright storageState 是什么?

Playwright storageState 是单个浏览器 context 存下来的 cookies 与 localStorage 快照。你在 context 已有要的状态后导出,再拿它去播种之后的 context,让它从这份快照起步,而不是空罐。

官方 Playwright 认证文档用这个文件做测试 setup。那是快照的消费者,不是快照的定义。这页只谈这个文件。

X 和 LinkedIn 的登录墙见 登录墙后方的 AI 抓取

JavaScript 抓取路线见 用 JavaScript 做网页抓取

storageState 文件实际存了什么?

这个文件是带 cookies 和 origins 的 JSON。cookies 是 cookie 对象数组。origins 是 origin 记录数组,每条带 localStorage 的 name/value。Playwright 的 storageState API 在你传 path 时写出这个形状;不传 path 时返回同一个对象。

存储区默认在 storageState 里?代表什么
cookies每条可带 name、value、domain、path、expires、httpOnly、secure、sameSite。
localStorage是,在 origins 下面按 origin 当键。存在 https://quotes.toscrape.com 的键,不会出现在另一个 origin。
sessionStorageJSON 默认没有 sessionStorage 键。还原后的页面读 sessionStorage 会得到 null。

我们在 2026-09-18 从 OpenCode 测过这个文件形状。headed Chromium 在 quotes.toscrape.com 导出的 storageState,顶层键是 cookies 和 origins。d05-demo 在 origins 的 localStorage 里。JSON 字符串不含 sessionStorage 或 d05-session。

OpenCode 正在导出 Playwright storageState,旁边是 headed Chromium 开着 quotes.toscrape.com
导出步骤:左边 OpenCode,右边独立 Chromium。没有登录表单。快照取自带假键的公开页。

Playwright 的认证指南也提到,部分设置的复用状态会含 IndexedDB 和 passkeys。别因为某篇博客列过,就假设那些键一定在。打开你生成的文件,读顶层键。

没有 Expires 或 Max-Age 的会话 cookie,是配置文件目录里另一个陷阱。磁盘行为见 持久浏览器会话。在 storageState JSON 里,cookie 过期是每条 cookie 的明确字段。0 或过去的时间戳,就是你看到这颗 cookie 撑不过下一个日历日的方式。

怎么生成 storageState 并加载?

从已经有你要的状态的 context 生成。在打开需要它的页面之前,加载到新 context。别从一个 origin 导出,却期待另一个 origin 的 localStorage 出现。

import { chromium } from "playwright";

const browser = await chromium.launch();
const setup = await browser.newContext();
const page = await setup.newPage();
await page.goto("https://quotes.toscrape.com/");
await page.evaluate(() => {
  localStorage.setItem("d05-demo", "local-only");
  sessionStorage.setItem("d05-session", "session-only");
});
await setup.storageState({ path: "playwright/.auth/user.json" });
await setup.close();

const reused = await browser.newContext({
  storageState: "playwright/.auth/user.json",
});
const next = await reused.newPage();
await next.goto("https://quotes.toscrape.com/");
const restored = await next.evaluate(() => ({
  local: localStorage.getItem("d05-demo"),
  session: sessionStorage.getItem("d05-session"),
}));
console.log(restored);
await browser.close();

那段代码就是整个机制。官方 Playwright 认证文档 会把它包进 setup project,让测试跳过登录界面。包装可选。那两个调用不行。

应用若只把会话 token 放在 sessionStorage,这个文件带不走。用 page.evaluate 复制 sessionStorage、让进程保持活着,或改用真实配置文件。

同一段 OpenCode 会话里,我们测过还原。从该文件加载的新 context 印出 local = local-only、session = null。

OpenCode 显示已还原的 localStorage 与空的 sessionStorage,旁边是 headed Chromium 开着 quotes.toscrape.com
还原检查:localStorage 回来了,sessionStorage 没有。左边 OpenCode,右边是公开名言页。

如何校验 storageState 是否生效?

用你写过的键证明还原,或用只有已存 cookies 才能打开的已认证 URL。别把「登录表单不见了」当证据。重定向错误也能把表单藏起来。

await page.goto("https://quotes.toscrape.com/");
const local = await page.evaluate(() => localStorage.getItem("d05-demo"));
if (local !== "local-only") {
  throw new Error("storageState did not restore localStorage");
}

真实账号就打一个只有 cookie 有效才回 200 的 URL,再断言看得见的已登录标签。落到 /login,快照就过期了。重新认证不在这页。这里只要失败信号:这份 JSON 没把会话还原回来。

检查通过失败
localStorage 键读到你存下的值加载后是 null
sessionStorage 键null,除非你自己复制过因为 localStorage 还在,就假设它也还在
Cookie 过期你需要的 cookies,expires 在未来expires 是 0,或已经过去

账号和环境要怎么隔离?

每个账号、每个环境、每个你打算复用的浏览器 context,各一份文件。把 staging 和 production 的 cookies 混进 user.json,就是在测错租户。

JSON 里的 origins 以 origin 为范围。存在 https://quotes.toscrape.com 的 localStorage 键,不会出现在 http://quotes.toscrape.com。scheme、host、port 都算。

playwright/.auth/staging-admin.json
playwright/.auth/staging-viewer.json
playwright/.auth/prod-readonly.json

并行 worker 各自需要自己的文件或自己的 context。两份 context 共用一份 JSON 再写回去,就是竞态。setup 后导出,运行期间只读加载,新文件只由专门的刷新任务写出。

怎么判断存下来的状态已过期?

文件看起来可以仍有效,站点却已撤销会话。先查 JSON 里的 cookie expires,再打一个需要那颗 cookie 的实时 URL。

expires: -1 或 0 的 cookie,在快照里是会话 cookie。同一次运行可能还能用,之后会不会消失取决于浏览器怎么对待它。过去的时间戳已经死了。未来的时间戳仍可能被服务端撤销。

真正要紧的是实时检查。加载后打开需要认证的路由。若拿到登录页、401 或匿名空壳,快照就用完了。刷新这个文件。这页不写「登录一次就永远能跑」的机器。

storageState 该怎么安全存放?

把这份 JSON 当密码。Playwright 自己的认证指南说,它可能含有能冒充你的 cookies 和请求头。别放进 git、日志、CI 产物,也别放进模型上下文。

# .gitignore
playwright/.auth/

CI 可以在任务开始时从密钥存储注入这个文件,结束时删掉。别打印出来。别附加到失败测试的 zip。别贴进 Agent 提示词去「调试会话」。

什么时候该改用真实浏览器配置文件?

应用需要的不只 cookies 和 localStorage 时,复用真实 Chromium 配置文件:扩展、sessionStorage、设备信号,或要人坐着过 MFA。JSON 文件带不走这些。

这就是 ego (lite) 0.5.0.32 适合的地方。Agent 在隔离的 Space 里,对准机器上已有的日常浏览器配置文件。标签页可以监视,提示可以接管,任务可以停。该版本的更新日志日期是 2026-09-12,见 ego (lite) 更新日志。它不是 storageState 导出器。文件形状不对时,它是跳过这个文件的那条路。

我们从 OpenCode 在 ego (lite) 测过这层隔离。Spaces 总览把名言任务放在自己正在跑的 Space,其他工作在另一个 Space,而不是用 JSON 重建会话。

OpenCode 旁边是 ego (lite) 的 Spaces 总览,名言 storageState 任务在自己的 Space 里跑
真实浏览器留在原地。一个 Space 跑名言任务;其他工作留在另一个 Space。

我们在其中一个 Space 里测过同一个公开 URL。Space 6 在 quotes.toscrape.com 维持 agent control,Take over 和 Stop 看得见。浏览器环境留在原地,而不是用 JSON 快照重建。

OpenCode 驱动 ego-browser,旁边是 ego (lite) Space 开着 quotes.toscrape.com,画面上有 Agent is in control、Take over 和 Stop
可监视的配置文件路线:左边 OpenCode,右边一个 ego (lite) Space,在公开名言页上仍由 Agent 控制。

localStorage 回来、sessionStorage 是 null,这个文件就做完它该做的。别把登录表单消失当证据。2026-09-18 还原后的 context 用假键印出 local = local-only、session = null,没有密码表单。

挑战和限制有哪些?

文件看起来完整,却仍漏掉应用真正在用的存储区。这就是默认失败。

第二是 origin 对不上。存在 localhost:3000,对 127.0.0.1:3000 加载,localStorage 是空的,cookies 则可能按 domain 仍会附上。

第三是把登录编舞塞进这页。检测 401、跳去 /login、刷新快照,都是真工作。那不是这个文件的工作。

常见问题

Playwright storageState 含 cookies 吗?

含。cookies 是 JSON 的顶层数组。每颗 cookie 可含 name、value、domain、path、expires、httpOnly、secure、sameSite。

storageState 含 localStorage 吗?

含,在 origins 下面。每条 origin 记录只放该 origin 的 localStorage name/value。

storageState 含 sessionStorage 吗?

默认不含。页面可以写 sessionStorage、导出 storageState,产出的 JSON 仍没有 sessionStorage 键。还原后的 context 读那个存储区是空的。2026-09-18 的 headed 运行还原出 local = local-only、session = null。

怎么生成 storageState 文件?

等 context 已有你要的 cookies 和 localStorage 后,调用 await context.storageState({ path: 'playwright/.auth/user.json' })。

怎么在新 context 加载 storageState?

把 storageState: 'playwright/.auth/user.json' 传进 browser.newContext。导航到需要这份快照的页面之前先加载。

如何校验 storageState 是否生效?

读你设过的 localStorage 键,或打开只有已存 cookies 才能到的 URL。登录界面不见了,不是证据。

storageState 该不该 commit 进 git?

不该。把 playwright/.auth/ 加进 .gitignore。这个文件可以冒充它捕捉到的账号。

两个测试能共用一份 storageState 文件吗?

可以只读加载同一份快照。别让并行 worker 写同一个文件。账号不同就每个角色一份文件。

什么时候该改用真实浏览器配置文件?

应用需要 sessionStorage、扩展,或 MFA 要人时。ego (lite) 在 Space 里对准你日常的 Chromium 配置文件跑这些工作。

storageState 等于 user data directory 吗?

不是。storageState 是 JSON 快照。user data directory 是磁盘上的配置文件。

若下一份任务需要真实已登录浏览器,而不是 JSON 快照,ego (lite) 可以免费下载