ego (lite) es solo un navegador, ego es su agente personal en todos sus dispositivos.
Únanse a la lista de espera
PlaywrightstorageStateCookieslocalStorageAutomatización del navegadorego (lite)

storageState de Playwright: qué guarda el JSON, cómo cargarlo y cómo demostrar que funcionó

18 sept 202611 min de lectura
Mascota verde de ego (lite) junto a trasteros azules con candado, sin saber cuál guarda la instantánea

Un archivo storageState de Playwright puede parecer completo y aun así no restaurar el estado del que depende la app. Pueden estar las cookies, puede estar localStorage y el JSON puede verse válido, pero sessionStorage falta por defecto. Si la app guarda parte de la sesión ahí, cargar el archivo en un contexto nuevo no trae de vuelta ese estado.

Esa distinción importa porque storageState es una instantánea, no un perfil completo de navegador. Playwright facilita exportarla, cargarla, aislarla por cuenta y verificarla. Pero cuando la tarea depende de estado que no cabe limpio en el archivo, como sessionStorage, extensiones, un perfil ya con sesión o que una persona complete el MFA, ego (lite) toma la otra ruta: dejar el entorno real de Chromium en su sitio en vez de reconstruirlo desde JSON.

Esta guía se queda en la instantánea: qué contiene storageState, cómo generarlo y cargarlo, cómo separar cuentas y entornos, y cómo demostrar que el estado restaurado funcionó de verdad. Los bucles de login y la reautenticación automática son otro problema. Aquí la pregunta es más simple: ¿qué guardó de verdad el archivo y qué dejó fuera?

¿Qué es storageState en Playwright?

storageState de Playwright es la instantánea guardada de cookies y localStorage de un contexto de navegador. La exportas cuando el contexto ya tiene el estado que quieres y luego siembras un contexto posterior para que arranque con esa instantánea en vez de un tarro vacío.

La documentación oficial de autenticación de Playwright monta el setup de tests sobre este archivo. Eso es un consumidor de la instantánea, no su definición. Esta página se queda en el archivo.

Los muros de login de X y LinkedIn se tratan en scraping con IA detrás de muros de login.

El enrutado de scrape en JavaScript se trata en web scraping con JavaScript.

¿Qué contiene de verdad el archivo storageState?

El archivo es JSON con cookies y origins. cookies es un array de objetos cookie. origins es un array de registros de origen, cada uno con pares name/value de localStorage. La API storageState escribe esa forma cuando pasas un path, y devuelve el mismo objeto cuando no lo haces.

Almacén¿En storageState por defecto?Qué significa
cookiesCada entrada puede llevar name, value, domain, path, expires, httpOnly, secure y sameSite.
localStorageSí, bajo originsIndexado por origen. Una clave guardada en https://quotes.toscrape.com no aparece en otro origen.
sessionStorageNoEl JSON no tiene clave sessionStorage por defecto. Una página restaurada lee sessionStorage como null.

Comprobamos esa forma de archivo el 2026-09-18 desde OpenCode. Chromium headed en quotes.toscrape.com exportó storageState con las claves de primer nivel cookies y origins. d05-demo estaba en localStorage de origins. La cadena JSON no contenía sessionStorage ni d05-session.

OpenCode exportando storageState de Playwright junto a Chromium headed en quotes.toscrape.com
El paso de exportar: OpenCode a la izquierda, Chromium independiente a la derecha. Sin formulario de login. La instantánea se toma de una página pública con claves ficticias.

La guía de autenticación de Playwright también menciona IndexedDB y passkeys en estado reutilizado para algunos setups. No asumas que esas claves existen porque un post las listó. Abre el archivo que generaste y lee las claves de primer nivel.

Las cookies de sesión sin Expires ni Max-Age son otra trampa en los directorios de perfil. Ese comportamiento en disco está en sesiones persistentes de navegador. En un JSON de storageState, la caducidad de la cookie es un campo explícito en cada cookie. Cero o una marca de tiempo pasada es cómo ves una cookie que no sobrevivirá al día siguiente.

¿Cómo se genera storageState y cómo se carga?

Genera desde un contexto que ya tenga el estado que quieres. Carga en un contexto nuevo antes de abrir la página que lo necesita. No exportes de un origen y esperes que aparezca el localStorage de otro.

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();

Ese fragmento es todo el mecanismo. La documentación de autenticación de Playwright lo envuelve en un proyecto de setup para que los tests se salten la UI de login. El envoltorio es opcional. Las dos llamadas no.

Si la app guarda el token de sesión solo en sessionStorage, este archivo no lo lleva. Copia sessionStorage con page.evaluate, deja un proceso vivo o usa un perfil real.

Comprobamos la restauración en la misma sesión de OpenCode. Un contexto nuevo cargado desde ese archivo imprimió local = local-only y session = null.

OpenCode muestra localStorage restaurado y sessionStorage vacío junto a Chromium headed en quotes.toscrape.com
La comprobación de restauración: localStorage volvió, sessionStorage no. OpenCode está a la izquierda; la página pública de citas, a la derecha.

¿Cómo se demuestra que el estado restaurado surtió efecto?

Demuestra la restauración con una clave que hayas escrito, o con una URL autenticada que solo las cookies guardadas puedan abrir. No trates 'falta el formulario de login' como prueba. Un bug de redirección también puede ocultar el formulario.

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");
}

Para una cuenta real, pega una URL que solo devuelva 200 cuando la cookie es válida y luego comprueba una etiqueta visible de sesión iniciada. Si caes en /login, la instantánea está caducada. La reautenticación es otro tema. Aquí solo necesitas el fallo: este JSON no restauró la sesión.

ComprobaciónPasaFalla
Clave de localStorageLee el valor que guardastenull después de cargar
Clave de sessionStoragenull salvo que la hayas copiado túAsumir que sobrevivió porque localStorage sí
Caducidad de cookieexpires está en el futuro en las cookies que necesitasexpires es 0 o ya pasó

¿Cómo se aíslan cuentas y entornos?

Un archivo por cuenta, por entorno y por contexto de navegador que pretendas reutilizar. Mezclar cookies de staging y producción en user.json es cómo terminas testeando el tenant equivocado.

Los origins del JSON están acotados por origen. Una clave de localStorage guardada en https://quotes.toscrape.com no aparece en http://quotes.toscrape.com. Cuentan el esquema, el host y el puerto.

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

Los workers en paralelo necesitan cada uno su archivo o su contexto. Compartir un JSON entre dos contextos que luego escriben de vuelta es una carrera. Exporta después del setup, carga en solo lectura durante la ejecución y escribe un archivo nuevo solo desde un trabajo dedicado de refresco.

¿Cómo se nota que el estado guardado caducó?

El archivo puede verse válido mientras el sitio ya revocó la sesión. Mira expires de las cookies en el JSON y luego una URL en vivo que exija esa cookie.

Una cookie con expires: -1 o 0 es una cookie de sesión en la instantánea. Puede funcionar en la misma ejecución y desaparecer después, según cómo la trate el navegador. Una marca de tiempo pasada ya está muerta. Una futura igual puede revocarse en el servidor.

La comprobación en vivo es la que importa. Abre una ruta autenticada después de cargar. Si obtienes la página de login, un 401 o un cascarón anónimo, la instantánea está gastada. Refresca el archivo. No programes en esta página una máquina de login único.

¿Cómo guardar storageState con seguridad?

Trata el JSON como una contraseña. La propia guía de autenticación de Playwright dice que puede contener cookies y headers con los que alguien se hace pasar por ti. Déjalo fuera de git, logs, artefactos de CI y el contexto del modelo.

# .gitignore
playwright/.auth/

CI puede inyectar el archivo desde un almacén de secretos al empezar el job y borrarlo al terminar. No lo imprimas. No lo adjuntres a un zip de tests fallidos. No lo pegues en un prompt de agente para 'depurar la sesión'.

¿Cuándo reutilizar un perfil real de navegador?

Reutiliza un perfil real de Chromium cuando la app necesita más que cookies y localStorage: extensiones, sessionStorage, señales del dispositivo o que una persona aguante el MFA. Un archivo JSON no puede llevar eso.

Ahí entra ego (lite) 0.5.0.32. El agente corre en un Space aislado contra el perfil diario de navegador que ya está en la máquina. Se puede vigilar la pestaña, tomar el control de un prompt y detener la tarea. El changelog de esa versión está fechado el 2026-09-12 en el changelog de ego (lite). No es un exportador de storageState. Es la ruta que se salta el archivo cuando el archivo tiene la forma equivocada.

Comprobamos ese aislamiento en ego (lite) desde OpenCode. La vista de Spaces dejó la tarea de citas en su propio Space en marcha, con el resto del trabajo en otro Space en vez de reconstruir la sesión desde JSON.

OpenCode junto a la vista de Spaces de ego (lite), con la tarea storageState de citas corriendo en su propio Space
El navegador real se queda en su sitio. Un Space corre la tarea de citas; el resto del trabajo se queda en otro Space.

Comprobamos la misma URL pública dentro de uno de esos Spaces. El Space 6 se quedó en control del agente en quotes.toscrape.com, con Take over y Stop visibles. El entorno del navegador se quedó en su sitio en vez de reconstruirse desde una instantánea JSON.

OpenCode conduce ego-browser junto a un Space de ego (lite) en quotes.toscrape.com, con Agent is in control, Take over y Stop
La ruta del perfil vigilado: OpenCode a la izquierda, un Space de ego (lite) a la derecha, todavía en control del agente en la página pública de citas.

Si localStorage vuelve y sessionStorage es null, el archivo hizo su trabajo. No trates un formulario de login ausente como prueba. El 2026-09-18 el contexto restaurado imprimió local = local-only y session = null desde claves ficticias, sin formulario de contraseña.

¿Cuáles son los desafíos y los límites?

El archivo se ve completo y aun así se pierde el almacén que la app usa de verdad. Ese es el fallo por defecto.

El desajuste de origen es el segundo. Guarda en localhost:3000, carga contra 127.0.0.1:3000, y localStorage queda vacío mientras las cookies quizá sigan adjuntándose según el dominio.

El tercero es recargar esta página con coreografía de login. Detectar un 401, rebotar a /login y refrescar la instantánea son trabajos reales. No son el trabajo de este archivo.

Preguntas frecuentes

¿storageState de Playwright incluye cookies?

Sí. cookies es un array de primer nivel en el JSON. Cada cookie puede incluir name, value, domain, path, expires, httpOnly, secure y sameSite.

¿storageState incluye localStorage?

Sí, bajo origins. Cada registro de origen guarda pares name/value de localStorage solo para ese origen.

¿storageState incluye sessionStorage?

No por defecto. Una página puede escribir sessionStorage, exportar storageState y aun así producir un JSON sin clave sessionStorage. El contexto restaurado lee ese almacén vacío. El 2026-09-18 la ejecución headed restauró local = local-only y session = null.

¿Cómo generar un archivo storageState?

Llama await context.storageState({ path: 'playwright/.auth/user.json' }) cuando el contexto ya tenga las cookies y el localStorage que quieres.

¿Cómo cargar storageState en un contexto nuevo?

Pasa storageState: 'playwright/.auth/user.json' a browser.newContext. Carga antes de navegar a la página que necesita la instantánea.

¿Cómo sé que el estado restaurado funcionó?

Lee una clave de localStorage que hayas puesto, o abre una URL a la que solo lleguen las cookies guardadas. Que falte la interfaz de login no es prueba.

¿Debo hacer commit de storageState a git?

No. Añade playwright/.auth/ a .gitignore. El archivo puede impersonar la cuenta que capturó.

¿Pueden dos tests compartir un archivo storageState?

Pueden cargar la misma instantánea en solo lectura. No dejes que workers en paralelo escriban el mismo archivo. Usa un archivo por rol si las cuentas son distintas.

¿Cuándo usar un perfil real de navegador?

Cuando la app necesita sessionStorage, extensiones o a una persona para el MFA. ego (lite) corre ese trabajo en un Space contra tu perfil diario de Chromium.

¿storageState es lo mismo que un user data directory?

No. storageState es una instantánea JSON. Un user data directory es el perfil en disco.

Si la siguiente tarea necesita el navegador real con sesión en vez de una instantánea JSON, ego (lite) es gratis para descargar.