
Un fichier Playwright storageState peut sembler complet et ne pas restaurer l'état dont une application dépend vraiment. Les cookies peuvent y être, le localStorage aussi, et le JSON peut paraître parfaitement valide, mais le sessionStorage manque par défaut. Si l'application y conserve une partie de sa session, charger le fichier dans un nouveau contexte ne ramènera pas cet état.
Cette distinction compte, parce que storageState est un instantané, pas un profil de navigateur complet. Playwright rend cet instantané facile à exporter, charger, isoler par compte et vérifier. Mais quand la tâche dépend d'un état de navigateur qui ne rentre pas proprement dans le fichier, comme le sessionStorage, des extensions, un profil déjà connecté ou un humain qui termine le MFA, ego (lite) prend l'autre voie : garder l'environnement Chromium réel en place plutôt que de le reconstruire à partir du JSON.
Ce guide reste sur l'instantané lui-même : ce que storageState contient, comment le générer et le charger, comment séparer comptes et environnements, et comment prouver que l'état restauré a réellement fonctionné. Les boucles de connexion et la réauthentification automatique sont un autre problème. Ici, la question est plus simple : qu'est-ce que le fichier a vraiment enregistré, et qu'est-ce qu'il a laissé de côté ?
Qu'est-ce que Playwright storageState ?
Playwright storageState est l'instantané cookies et localStorage d'un contexte de navigateur. Vous l'exportez une fois que le contexte a déjà l'état voulu, puis vous en semez un contexte ultérieur pour qu'il démarre avec cet instantané plutôt qu'avec un magasin vide.
La documentation officielle d'authentification Playwright construit le setup de test sur ce fichier. C'est un consommateur de l'instantané, pas la définition de l'instantané. Cette page reste sur le fichier.
Les murs de connexion sur X et LinkedIn sont traités dans scraping IA derrière des murs de connexion.
Le choix de voie pour un scrape JavaScript est traité dans web scraping en JavaScript.
Que contient réellement le fichier storageState ?
Le fichier est du JSON avec cookies et origins. cookies est un tableau d'objets cookie. origins est un tableau d'enregistrements d'origine, chacun avec des paires nom/valeur de localStorage. L'API storageState API de Playwright écrit cette forme quand vous passez un chemin, et renvoie le même objet quand vous ne le faites pas.
| Stockage | Dans storageState par défaut ? | Ce que cela signifie |
|---|---|---|
| cookies | Oui | Chaque entrée peut porter name, value, domain, path, expires, httpOnly, secure et sameSite. |
| localStorage | Oui, sous origins | Indexé par origine. Une clé enregistrée sur https://quotes.toscrape.com n'apparaît pas sur une autre origine. |
| sessionStorage | Non | Le JSON n'a pas de clé sessionStorage par défaut. Une page restaurée lit sessionStorage comme null. |
Nous avons testé cette forme de fichier le 2026-09-18 depuis OpenCode. Chromium headed sur quotes.toscrape.com a exporté storageState avec les clés de premier niveau cookies et origins. d05-demo était dans origins localStorage. La chaîne JSON ne contenait ni sessionStorage ni d05-session.

Le guide d'authentification Playwright mentionne aussi IndexedDB et les passkeys dans l'état réutilisé pour certains setups. Ne supposez pas que ces clés existent parce qu'un article de blog les a listées. Ouvrez le fichier que vous avez généré et lisez les clés de premier niveau.
Les cookies de session sans Expires ni Max-Age sont un piège distinct dans les répertoires de profil. Ce comportement disque est traité dans sessions de navigateur persistantes. Dans un JSON storageState, l'expiration du cookie est un champ explicite sur chaque cookie. Zéro ou un horodatage passé, c'est ainsi que vous voyez un cookie qui ne survivra pas au jour calendaire suivant.
Comment générer storageState et le charger ?
Générez depuis un contexte qui a déjà l'état voulu. Chargez dans un nouveau contexte avant d'ouvrir la page qui en a besoin. N'exportez pas depuis une origine en attendant le localStorage d'une autre.
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();Ce fragment est tout le mécanisme. La documentation d'authentification Playwright officielle l'enveloppe dans un projet de setup pour que les tests sautent l'interface de connexion. L'enveloppe est optionnelle. Les deux appels ne le sont pas.
Si l'application ne garde le jeton de session que dans sessionStorage, ce fichier ne l'emportera pas. Copiez sessionStorage avec page.evaluate, gardez un processus en vie, ou utilisez un vrai profil.
Nous avons testé la restauration dans la même session OpenCode. Un nouveau contexte chargé depuis ce fichier a imprimé local = local-only et session = null.

Comment prouver que l'état restauré a bien pris effet ?
Prouvez la restauration avec une clé que vous avez écrite, ou avec une URL authentifiée que seuls les cookies enregistrés peuvent ouvrir. Ne traitez pas « le formulaire de connexion a disparu » comme une preuve. Un bug de redirection peut aussi cacher le formulaire.
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");
}Pour un vrai compte, interrogez une URL qui renvoie 200 seulement quand le cookie est valide, puis affirmez un libellé visible de connexion. Si vous atterrissez sur /login, l'instantané est périmé. La réauthentification appartient ailleurs. Ici, vous n'avez besoin que de l'échec : ce JSON n'a pas restauré la session.
| Contrôle | Réussi | Échec |
|---|---|---|
| Clé localStorage | Lit la valeur que vous avez enregistrée | null après chargement |
| Clé sessionStorage | null sauf si vous l'avez copiée vous-même | Supposer qu'elle a survécu parce que le localStorage a survécu |
| Expiration du cookie | expires est dans le futur sur les cookies dont vous avez besoin | expires vaut 0 ou est déjà passé |
Comment isoler les comptes et les environnements ?
Un fichier par compte, par environnement, par contexte de navigateur que vous comptez réutiliser. Mélanger cookies de staging et de production dans user.json, c'est tester le mauvais locataire.
Les origines dans le JSON sont limitées à l'origine. Une clé localStorage enregistrée sur https://quotes.toscrape.com n'apparaîtra pas sur http://quotes.toscrape.com. Le schéma, l'hôte et le port comptent tous.
playwright/.auth/staging-admin.json
playwright/.auth/staging-viewer.json
playwright/.auth/prod-readonly.jsonLes workers parallèles ont chacun besoin de leur propre fichier ou de leur propre contexte. Partager un JSON entre deux contextes qui réécrivent ensuite, c'est une course. Exportez après le setup, chargez en lecture seule pendant l'exécution, et n'écrivez un nouveau fichier que depuis un job de rafraîchissement dédié.
Comment savoir que l'état enregistré a expiré ?
Le fichier peut sembler valide alors que le site a déjà révoqué la session. Vérifiez cookie expires dans le JSON, puis une URL en direct qui exige ce cookie.
Un cookie avec expires: -1 ou 0 est un cookie de session dans l'instantané. Il peut fonctionner dans la même exécution et disparaître plus tard selon le traitement du navigateur. Un horodatage passé est déjà mort. Un horodatage futur peut encore être révoqué côté serveur.
Le contrôle en direct est celui qui compte. Ouvrez une route authentifiée après le chargement. Si vous obtenez la page de connexion, un 401 ou une coquille anonyme, l'instantané est usé. Rafraîchissez le fichier. N'encodez pas une machine de connexion unique sur cette page.
Comment stocker storageState en toute sécurité ?
Traitez le JSON comme un mot de passe. Le guide d'authentification de Playwright lui-même dit qu'il peut contenir des cookies et des en-têtes qui permettent de se faire passer pour vous. Tenez-le hors de git, des journaux, des artefacts CI et du contexte du modèle.
# .gitignore
playwright/.auth/La CI peut injecter le fichier depuis un magasin de secrets au début du job et le supprimer à la fin. Ne l'imprimez pas. Ne l'attachez pas à un zip de test en échec. Ne le collez pas dans une invite d'agent pour « déboguer la session ».
Quand réutiliser plutôt un vrai profil de navigateur ?
Réutilisez un vrai profil Chromium quand l'application a besoin de plus que des cookies et du localStorage : extensions, sessionStorage, signaux d'appareil, ou un humain qui traverse le MFA. Un fichier JSON ne peut pas porter cela.
C'est là que ego (lite) 0.5.0.32 s'inscrit. L'agent s'exécute dans un Space isolé contre le profil de navigateur quotidien déjà sur la machine. L'onglet peut être observé, une invite reprise, et la tâche arrêtée. Le changelog de cette version est daté du 2026-09-12 sur le changelog ego (lite). Ce n'est pas un exportateur storageState. C'est la voie qui saute le fichier quand le fichier a la mauvaise forme.
Nous avons testé cette isolation dans ego (lite) depuis OpenCode. La vue d'ensemble des Spaces a gardé la tâche citations dans son propre Space en cours, avec le reste du travail dans un Space séparé, au lieu de reconstruire la session à partir du JSON.

Nous avons testé la même URL publique dans l'un de ces Spaces. Space 6 est resté sous contrôle de l'agent sur quotes.toscrape.com, avec Take over et Stop visibles. L'environnement du navigateur est resté en place au lieu d'être reconstruit à partir d'un instantané JSON.

Si le localStorage revient et que sessionStorage est null, le fichier a fait son travail. Ne traitez pas un formulaire de connexion manquant comme une preuve. Le 2026-09-18, le contexte restauré a imprimé local = local-only et session = null à partir de clés factices, sans formulaire de mot de passe.
Quels sont les défis et les limites ?
Le fichier a l'air complet et manque encore le magasin que votre application utilise vraiment. C'est l'échec par défaut.
Le décalage d'origine est le deuxième. Enregistrez sur localhost:3000, chargez contre 127.0.0.1:3000, et le localStorage est vide alors que les cookies peuvent encore s'attacher selon le domaine.
Le troisième est de surcharger cette page avec une chorégraphie de connexion. Détecter un 401, rebondir vers /login et rafraîchir l'instantané sont de vrais travaux. Ce n'est pas le travail de ce fichier.
FAQ
Playwright storageState inclut-il les cookies ?
Oui. cookies est un tableau de premier niveau dans le JSON. Chaque cookie peut inclure name, value, domain, path, expires, httpOnly, secure et sameSite.
storageState inclut-il le localStorage ?
Oui, sous origins. Chaque enregistrement d'origine contient des paires nom/valeur de localStorage pour cette origine seulement.
storageState inclut-il le sessionStorage ?
Pas par défaut. Une page peut écrire dans sessionStorage, exporter storageState, et produire quand même un JSON sans clé sessionStorage. Le contexte restauré lit ce magasin comme vide. Le 2026-09-18, l'exécution headed a restauré local = local-only et session = null.
Comment générer un fichier storageState ?
Appelez await context.storageState({ path: 'playwright/.auth/user.json' }) une fois que le contexte contient déjà les cookies et le localStorage voulus.
Comment charger storageState dans un nouveau contexte ?
Passez storageState: 'playwright/.auth/user.json' à browser.newContext. Chargez avant de naviguer vers la page qui a besoin de l'instantané.
Comment prouver que storageState a bien restauré localStorage ?
Lisez une clé localStorage que vous avez posée, ou ouvrez une URL que seuls les cookies enregistrés peuvent atteindre. L'absence du formulaire de connexion n'est pas une preuve.
Dois-je committer storageState dans git ?
Non. Ajoutez playwright/.auth/ à .gitignore. Le fichier permet de se faire passer pour le compte qu'il a capturé.
Deux tests peuvent-ils partager un fichier storageState ?
Ils peuvent charger le même instantané en lecture seule. Ne laissez pas des workers parallèles écrire le même fichier. Utilisez un fichier par rôle si les comptes diffèrent.
Quand utiliser plutôt un vrai profil de navigateur ?
Quand l'application a besoin de sessionStorage, d'extensions ou d'un humain pour le MFA. ego (lite) exécute ce travail dans un Space contre votre profil Chromium quotidien.
storageState est-il la même chose qu'un répertoire user data ?
Non. storageState est un instantané JSON. Un répertoire user data est le profil sur disque.
Si la tâche suivante a besoin du vrai navigateur connecté plutôt que d'un instantané JSON, ego (lite) est gratuit à télécharger.
