
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。 |
| sessionStorage | 否 | JSON 預設沒有 sessionStorage 鍵。還原後的頁面讀 sessionStorage 會得到 null。 |
我們在 2026-09-18 從 OpenCode 測過這個檔形狀。headed Chromium 在 quotes.toscrape.com 匯出的 storageState,頂層鍵是 cookies 與 origins。d05-demo 在 origins 的 localStorage 裡。JSON 字串不含 sessionStorage 或 d05-session。

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。

如何驗證 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 重建工作階段。

我們在其中一個 Space 裡測過同一個公開 URL。Space 6 在 quotes.toscrape.com 維持 agent control,Take over 與 Stop 看得到。瀏覽器環境留在原地,而不是用 JSON 快照重建。

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) 可免費下載。
