ego (lite) はブラウザそのもの、ego は端末を横断して動くあなた専属のエージェントです。
順番待ちリストに参加する
PlaywrightstorageStateCookieslocalStorageブラウザ自動化ego (lite)

Playwright Storage State:JSONが保存するもの、読み込み方、復元の証明

2026年9月18日11 分で読了
鍵のかかった青い保管庫の横で、スナップショットが入っている区画が分からず困っている緑のego (lite)マスコット

PlaywrightのstorageStateファイルは、揃って見えるのに、アプリが本当に頼っている状態を復元できないことがあります。cookiesもlocalStorageもあり、JSONも問題なく見えても、sessionStorageは既定では欠けます。アプリがセッションの一部をそこに置いているなら、新しいcontextにファイルを読み込んでも、その状態は戻りません。

この区別が効くのは、storageStateがスナップショットであり、完全なブラウザプロファイルではないからです。Playwrightはそのスナップショットを書き出し、読み込み、アカウントごとに分離し、検証しやすくします。ただし作業がsessionStorage、拡張機能、既存のログイン済みプロファイル、人がMFAを完了することなど、ファイルにきれいに収まらないブラウザ状態に依存するなら、ego (lite)は別の経路を取ります。JSONから作り直すのではなく、実Chromium環境をそのまま置きます。

このガイドが扱うのはスナップショットそのものです。storageStateに何が入るか、どう生成して読み込むか、アカウントと環境をどう分けるか、復元が本当に効いたかをどう証明するかです。ログインループと自動再認証は別の問題です。ここでの問いはもっと単純です。ファイルは何を本当に保存し、何を残したのか。

PlaywrightのstorageStateとは

PlaywrightのstorageStateは、1つのブラウザcontextのcookieとlocalStorageを保存したスナップショットです。欲しい状態がすでに入ったcontextから書き出し、後のcontextへ流し込み、空の状態ではなくそのスナップショットから始めます。

公式のPlaywright認証ドキュメントは、このファイルの上にテスト準備を組みます。それはスナップショットの利用者であり、定義ではありません。このページが扱うのはファイルです。

XやLinkedInのログイン壁は次の記事で扱います: ログイン壁の向こうのAIスクレイピング

JavaScriptスクレイピングの経路分けは次の記事で扱います: JavaScriptによるWebスクレイピング

storageStateファイルに実際入っているもの

ファイルはcookiesとoriginsを持つJSONです。cookiesはcookieオブジェクトの配列です。originsはoriginレコードの配列で、それぞれlocalStorageのname/valueペアを持ちます。Playwrightの storageState APIはパスを渡すとその形を書き、渡さないと同じオブジェクトを返します。

ストア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から確認しました。quotes.toscrape.comのheaded Chromiumは、トップレベルキーcookiesとoriginsのstorageStateを書き出しました。d05-demoはoriginsのlocalStorageにありました。JSON文字列にsessionStorageもd05-sessionもありませんでした。

quotes.toscrape.comのheaded Chromiumの横で、Playwright storageStateを書き出すOpenCode
書き出しの手順。左が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 authentication docsはsetupプロジェクトに包み、テストがログインUIを飛ばせるようにします。包みは任意です。2つの呼び出しは任意ではありません。

アプリがセッショントークンをsessionStorageにしか置かないなら、このファイルは運びません。page.evaluateでsessionStorageをコピーするか、プロセスを生かし続けるか、実プロファイルを使います。

復元も同じOpenCodeセッションで確認しました。そのファイルから読み込んだ新しいcontextは、local = local-only、session = nullと出力しました。

復元されたlocalStorageと空のsessionStorageを示すOpenCodeと、quotes.toscrape.comのheaded Chromium
復元の確認。localStorageは戻り、sessionStorageは戻りませんでした。左がOpenCode、右が公開のquotesページです。

復元が効いたことを証明する方法

復元の証明は、書いたキーを読むか、保存した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キー自分でコピーしない限りnulllocalStorageが残ったからこちらも残ったと仮定する
Cookieの有効期限必要なcookiesのexpiresが未来であるexpiresが0、またはすでに過去

アカウントと環境を分離する方法

再利用するアカウント、環境、ブラウザcontextごとにファイルを1つ。user.jsonにステージングと本番のcookiesを混ぜると、間違ったテナントをテストします。

JSONのoriginsはoriginスコープです。https://quotes.toscrape.comに保存したlocalStorageキーは、http://quotes.toscrape.comには出ません。スキーム、ホスト、ポートのすべてが効きます。

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

並行ワーカーはそれぞれ自分のファイルか自分のcontextが必要です。書き戻す2つのcontextで1つのJSONを共有すると競合します。準備のあとで書き出し、実行中は読み取り専用で読み込み、新しいファイルを書くのは専用の更新ジョブだけにします。

保存した状態の期限切れを見抜く方法

ファイルは有効に見えても、サイトがすでにセッションを取り消していることがあります。JSONのcookie expiresを見てから、そのcookieが必要なライブURLを確認します。

expires: -1または0のcookieは、スナップショット内のセッションcookieです。同じ実行では動いても、ブラウザの扱いで後から消えることがあります。過去のタイムスタンプはすでに死んでいます。未来のタイムスタンプでも、サーバー側で取り消されることがあります。

効くのはライブ確認です。読み込み後に認証済みルートを開きます。ログインページ、401、匿名シェルが返ったら、スナップショットは使い果たされています。ファイルを更新してください。このページで一度きりのログイン機械を組まないでください。

storageStateを安全に置く方法

JSONはパスワードとして扱います。Playwright自身の認証ガイドも、本人になりすますcookiesとヘッダーが入ると書いています。git、ログ、CI成果物、モデルのコンテキストに入れないでください。

# .gitignore
playwright/.auth/

CIはジョブ開始時にシークレットストアからファイルを注入し、終了時に削除できます。印刷しないでください。失敗テストのzipに付けないでください。「セッションをデバッグする」ためにエージェントのプロンプトへ貼らないでください。

実ブラウザプロファイルを再利用すべきなのはどんなときか

アプリがcookiesとlocalStorage以上を必要とするなら、実Chromiumプロファイルを再利用します。拡張機能、sessionStorage、端末シグナル、人がMFAをやり遂げることです。JSONファイルはそれを運べません。

そこで合うのが ego (lite) 0.5.0.32です。エージェントは、マシン上の日常ブラウザプロファイルに対して、分離したSpaceで動きます。タブを見守れ、プロンプトを引き継げ、タスクを止められます。このバージョンのChangelog日付は2026-09-12です。詳細はego (lite)の更新履歴にあります。storageStateの書き出しツールではありません。ファイルの形が合わないときに、ファイルを飛ばす経路です。

その分離は、OpenCodeからego (lite)で確認しました。Spacesの概要はquotesの作業を単独の実行中Spaceに置き、他の作業は別Spaceへ分けていました。セッションをJSONから作り直してはいません。

quotesのstorageState作業が単独Spaceで動いているego (lite)のSpaces概要と、その横のOpenCode
実ブラウザはそのままです。1つのSpaceがquotesの作業を実行し、他の作業は別Spaceに残ります。

同じ公開URLを、そうしたSpaceの1つで確認しました。Space 6はquotes.toscrape.comでエージェント制御のままで、Take overとStopが見えました。ブラウザ環境はそのままで、JSONスナップショットから組み立て直してはいません。

quotes.toscrape.comのego (lite) SpaceでAgent is in control、Take over、Stopが見える状態を、横のOpenCodeがego-browser経由で操作している
見守れるプロファイル経路。左がOpenCode、右が1つのego (lite) Space。公開quotesページ上でもエージェント制御のままです。

localStorageが戻り、sessionStorageがnullなら、ファイルは役目を果たしています。消えたログインフォームを証拠にしないでください。2026-09-18、復元したcontextはダミーキーからlocal = local-only、session = nullと出力し、パスワードフォームはありませんでした。

課題と制限

ファイルは揃って見えても、アプリが本当に使うストアを欠きます。それが既定の失敗です。

2つ目はoriginの不一致です。localhost:3000に保存して127.0.0.1:3000へ読み込むと、localStorageは空で、cookiesはドメイン次第で付くことがあります。

3つ目は、このページにログインの振り付けを載せすぎることです。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を書き出しても、sessionStorageキーのないJSONができます。復元したcontextはそのストアを空として読みます。2026-09-18のheaded実行はlocal = local-only、session = nullを復元しました。

storageStateファイルの作り方

欲しいcookiesとlocalStorageがすでに入ったcontextのあとで、await context.storageState({ path: 'playwright/.auth/user.json' })を呼びます。

新しいcontextへstorageStateを読み込む方法

browser.newContextにstorageState: 'playwright/.auth/user.json'を渡します。スナップショットが必要なページへ遷移する前に読み込みます。

復元が効いたかの見分け方

設定したlocalStorageキーを読むか、保存したcookiesだけが到達できるURLを開きます。ログイン画面がないことは証拠ではありません。

storageStateをgitにコミットしてよいか

いいえ。playwright/.auth/を.gitignoreに追加します。ファイルは取り込んだアカウントになりすませます。

2つのテストでstorageStateファイルを1つ共有できるか

同じスナップショットを読み取り専用で読み込めます。並行ワーカーに同じファイルを書かせないでください。アカウントが違うならロールごとにファイルを分けます。

実ブラウザプロファイルを使うべきなのはどんなとき

アプリがsessionStorage、拡張機能、MFAのための人を必要とするときです。ego (lite)はその作業を、日常のChromiumプロファイルに対するSpaceで実行します。

storageStateはuser data directoryと同じか

違います。storageStateはJSONスナップショットです。user data directoryはディスク上のプロファイルです。

次の作業がJSONスナップショットではなく、実際のログイン済みブラウザを必要とするなら、ego (lite) は無料でダウンロードできます