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