Identidad

Cookies, localStorage e IndexedDB: dónde debe vivir el estado

Compara cookies, Web Storage e IndexedDB por ámbito, exposición en la red, capacidad y expulsión, y decide dónde debe vivir cada dato de estado.

Documentación

Quieres la documentación estructurada de Identidad?

Este artículo forma parte de la biblioteca editorial. Para pasos de configuración, material de referencia y actualizaciones continuas, entra en la sección de docs.

Qué guarda cada mecanismo de almacenamiento

Una página dispone de cuatro lugares habituales para guardar estado, y se distinguen por quién puede leer los datos, cuándo viajan por la red y cuánto tiempo duran. Las cookies son pequeños pares nombre-valor que pueden establecer tanto el servidor como la página. Web Storage tiene dos partes, localStorage y sessionStorage, y guarda pares clave-valor de texto para los scripts. IndexedDB es una base de datos asíncrona y transaccional para datos estructurados. Elegir entre ellos es una decisión sobre ciclo de vida y exposición, no una cuestión de costumbre.

Comparación de cookies, localStorage, sessionStorage e IndexedDB según exposición en la red, ámbito, duración y expulsión, con un depósito de almacenamiento de mejor esfuerzo

Las cookies se definen en la RFC 6265. Un servidor establece una con una cabecera de respuesta Set-Cookie, o un script lo hace mediante document.cookie, y el navegador adjunta las cookies que coinciden a las solicitudes posteriores en una cabecera Cookie. Atributos como Domain, Path, Secure y HttpOnly, además del atributo SameSite posterior que describe MDN, acotan dónde se envía una cookie y quién puede leerla. Una cookie marcada con HttpOnly no puede ser leída por el script de la página, por eso resulta adecuada para un identificador de sesión emitido por el servidor. La guía de cookies de MDN describe cómo se comportan estos atributos en los navegadores actuales.

Web Storage se especifica en el estándar HTML. localStorage y sessionStorage exponen la misma interfaz síncrona de getItem, setItem, removeItem y clear, con claves y valores de texto. Cualquier dato más complejo, como un objeto o una lista, debe serializarlo la aplicación. Como las llamadas son síncronas, las lecturas y escrituras grandes en el hilo principal pueden retrasar el renderizado, así que Web Storage conviene para valores pequeños.

IndexedDB lo especifica el W3C. Guarda valores estructurados, incluidos archivos y blobs, en almacenes de objetos que pueden tener índices, y lee y escribe mediante solicitudes asíncronas dentro de transacciones. Una base de datos lleva un número de versión, y un cambio de esquema se ejecuta en un paso de actualización que controla la aplicación. También está disponible en workers, de modo que las lecturas pesadas no tienen que ocupar el hilo principal. El precio es más código y más estados que gestionar que con una llamada de una línea a localStorage.

El almacenamiento por origen abarca además otros depósitos, como la Cache API y los registros de service workers, que conviven con estos mecanismos y están sujetos a las mismas reglas de cuota y expulsión en los navegadores que implementan el Storage Standard. Por tanto, una limpieza o una expulsión puede eliminar más que los almacenes comparados aquí, y quien revise no debe suponer que un valor de un almacén sobrevive a los demás.

Ámbito y exposición en la red

El ámbito es donde más difieren los mecanismos. Web Storage e IndexedDB tienen ámbito de origen, la combinación de esquema, host y puerto, de modo que https://example.com y https://example.com:8443 guardan datos distintos. Las cookies, en cambio, tienen ámbito de host y ruta, y la especificación de cookies no las separa por puerto; el atributo Secure es el único control relacionado con el esquema. SameSite añade una noción distinta de sitio, un dominio registrable, que determina si una cookie acompaña a las solicitudes entre sitios. Mantén separados origen y sitio cuando razones sobre qué código puede ver un valor.

La exposición en la red se deduce de lo anterior. De los cuatro mecanismos, solo las cookies se adjuntan automáticamente a las solicitudes HTTP, de modo que cada solicitud coincidente las lleva, las necesite o no el servidor. Eso es útil para un identificador de sesión que el servidor debe leer en cada solicitud, y costoso para cualquier dato grande, porque los bytes viajan con cada solicitud a ese host. localStorage, sessionStorage e IndexedDB no salen del navegador a menos que el código de la aplicación lea un valor y lo envíe. La diferencia también decide qué lado puede actuar: un servidor puede leer una cookie sin ejecutar ningún script, pero no puede ver Web Storage ni IndexedDB.

La exposición a los scripts es la imagen inversa. Cualquier script que se ejecute en un origen puede leer el localStorage, el sessionStorage y el IndexedDB de ese origen, incluidos los scripts de terceros que la página incorpora y cualquier script inyectado por un fallo de cross-site scripting. Una cookie marcada con HttpOnly se oculta a los scripts, así que es el lugar más seguro para una credencial. Un token de portador guardado en localStorage lo puede leer el mismo código que dibuja la página. Registra esto como una decisión de diseño con sus compensaciones; no es motivo para evitar Web Storage en preferencias ordinarias.

El tamaño de las cookies merece una nota propia. Como las cookies viajan con las solicitudes, un conjunto creciente de cookies agranda cada solicitud, y los navegadores imponen sus propios límites de tamaño y de número de cookies por host. La RFC 6265 pide a los agentes de usuario que admitan solo mínimos modestos, así que una cookie debe llevar un identificador o un indicador breve y dejar los datos mayores a un registro del servidor o al almacenamiento del lado del cliente.

Los contextos incrustados y de terceros añaden otra capa. Los navegadores particionan cada vez más el almacenamiento y las cookies según el sitio de nivel superior, así que un marco incrustado puede ver un depósito distinto del que ve el mismo origen como página de nivel superior. Los detalles varían según el navegador y la versión. El artículo sobre partición del almacenamiento del navegador y privacidad explica cómo probarlo. Cuando una función dependa del estado dentro de un marco incrustado, pruébala en esa posición incrustada y no supongas que el resultado del nivel superior se traslada.

Duración, capacidad y expulsión

La duración tiene dos extremos: el punto en que el estado deja de estar disponible por diseño y el punto en que el navegador lo elimina. Las cookies de sesión, las que no llevan Expires ni Max-Age, duran hasta que termina la sesión del navegador, un límite que define el propio navegador, y algunos navegadores restauran sesiones y las conservan tras un reinicio. Las cookies persistentes duran hasta su fecha de caducidad, o hasta que el usuario o el navegador las elimina. localStorage e IndexedDB no tienen caducidad propia y permanecen hasta que un script, el usuario o el navegador los borra. sessionStorage dura lo que su contexto de navegación de nivel superior, aproximadamente una pestaña, y sobrevive a las recargas pero no al cierre de la pestaña.

Cerrar cosas muestra la diferencia con claridad. Cerrar una pestaña termina el sessionStorage de esa pestaña, pero deja en su sitio una cookie de sesión, localStorage e IndexedDB. Cerrar todo el navegador suele terminar las cookies de sesión, aunque la restauración de sesión puede devolverlas, y deja localStorage e IndexedDB. Una segunda pestaña abierta de forma independiente en el mismo origen comparte cookies, localStorage e IndexedDB, pero tiene su propio sessionStorage (una ventana abierta por script empieza con una copia). Un evento storage también avisa a los demás documentos del mismo origen cuando cambia localStorage, y así pueden mantenerse sincronizadas las pestañas.

La capacidad también difiere. Las cookies se limitan a valores pequeños y a un número limitado por host. Web Storage suele permitir unos pocos megabytes por origen, e IndexedDB permite mucho más, dentro de una cuota que el navegador deriva del tamaño total del disco. Estas cifras dependen del navegador, y la página de MDN sobre cuotas de almacenamiento y criterios de expulsión lo dice con claridad. Una aplicación debe leer sus límites en tiempo de ejecución y gestionar un error de cuota, en lugar de fijar una suposición tomada de un solo navegador.

El Storage Standard añade la regla más importante para el diseño: la persistencia es de mejor esfuerzo por defecto. Cada origen tiene un depósito de almacenamiento, y según el Storage Standard el navegador puede borrar un depósito de mejor esfuerzo cuando necesita espacio, eliminando los datos del origen como una unidad y sin prometer preguntar antes. Una aplicación puede llamar a navigator.storage.persist() para solicitar almacenamiento persistente, y el navegador decide si lo concede según una política propia que puede implicar un aviso al usuario. navigator.storage.estimate() informa de un uso y una cuota aproximados, y navigator.storage.persisted() indica si el depósito es persistente. Trata las tres como indicios, no como garantías.

La expulsión no es la única forma de que el estado desaparezca. Los usuarios borran los datos del sitio, los modos privados descartan el almacenamiento al cerrar la ventana, un sitio puede enviar una cabecera de respuesta Clear-Site-Data para pedir al navegador que borre las cookies o el almacenamiento de su propio origen, y una actualización del navegador o un cambio de perfil puede reiniciar lo que contiene un perfil. Las políticas también cambian entre versiones, así que un comportamiento observado una vez no es una promesa. Las especificaciones y MDN describen estos comportamientos como dependientes del navegador, y nada de lo aquí expuesto promete cuotas, caducidad o expulsión idénticas entre navegadores, versiones o modos privados.

Los datos guardados también sobreviven al código que los escribió. Cuando una versión cambia la forma de un valor guardado, las entradas antiguas deben leerse, migrarse o descartarse, y un cambio de esquema de IndexedDB necesita un aumento de versión y un paso de actualización. Un campo de versión dentro de los valores de localStorage cumple el mismo propósito. Un equipo que se salta este paso lo descubre la primera vez que un navegador que vuelve carga datos escritos por una versión anterior.

Como la persistencia es de mejor esfuerzo, la aplicación debe tratar el almacenamiento del navegador como una caché de estado que puede reconstruir, salvo que los datos sean la única copia y el usuario esté informado. Cuando falte un valor, la aplicación debe recurrir a un valor por defecto documentado, volver a obtener el estado del servidor o pedir al usuario que inicie sesión o que vuelva a escribir un borrador. Envuelve las lecturas y escrituras en gestión de errores, porque una escritura puede lanzar un error de cuota y algunos contextos deniegan el almacenamiento por completo, y asegúrate de que la ruta de primer uso funcione con un almacén vacío.

Elegir un mecanismo para cada dato de estado

Parte del estado, no de la API. Para un identificador de sesión que el servidor debe leer en cada solicitud, usa una cookie con Secure, HttpOnly y un valor de SameSite adecuado, además de una duración que el servidor pueda aplicar y revocar. Dos compensaciones guían esa elección: la exposición en la red y la exposición a los scripts. La entrega automática y la protección frente a la lectura por scripts pesan más que el límite de tamaño, porque un identificador es diminuto.

Para una preferencia pequeña, como un tema, un idioma o un aviso descartado, localStorage suele bastar. El valor es un texto breve, solo lo necesita el código del cliente y perderlo le cuesta al usuario un clic. Si el servidor debe generar la primera respuesta con esa preferencia, una cookie es mejor lugar porque el servidor puede verla; en caso contrario, evita meter las preferencias en cada solicitud. Usa sessionStorage cuando el valor deba terminar con la pestaña, por ejemplo un paso de formulario a medias que no debe aparecer en otra pestaña.

Para datos estructurados sin conexión, como una cola de ediciones sin enviar, registros en caché o archivos, usa IndexedDB. Admite mayores volúmenes, índices y transacciones, y funciona desde workers. Su precio es la regla del mejor esfuerzo: una aplicación que guarda allí la única copia de una edición sin enviar depende de un depósito que el navegador puede expulsar. Marca esos datos como pendientes en la interfaz, sincronízalos con el servidor cuando sea posible y solicita almacenamiento persistente solo cuando los datos lo justifiquen.

Un diseño mixto es normal y a menudo correcto. Un producto puede mantener una cookie de sesión HttpOnly, una preferencia de tema en localStorage y una cola sin conexión en IndexedDB, cada una elegida por su ciclo de vida. Lo que el diseño debe evitar es duplicar el mismo valor en varios sitios sin una regla sobre qué copia prevalece, porque las copias se desajustan cuando una se expulsa o se borra y otra no. Nombra un único responsable para cada dato de estado y trata las demás copias como derivadas.

Un valor ausente merece una lectura cuidadosa. Una cookie ausente o un localStorage vacío solo indica a una aplicación que este navegador no conservó ese valor o nunca lo recibió. No dice nada fiable sobre quién es el usuario, si la visita es la primera o qué tipo de cliente se está ejecutando, así que no debe convertirse en un juicio de identidad o de confianza. Úsalo para decidir qué mostrar o reconstruir, y deja las decisiones sobre personas a la autenticación. Esta comparación sirve para diseñar y revisar el almacenamiento de tu propia aplicación; leer, copiar o sustituir el estado guardado por otro sitio queda fuera de su alcance.

Revisar el almacenamiento en un flujo con varios contextos

Los equipos que ejecutan el mismo recorrido en varios contextos del navegador necesitan saber con qué estado empieza cada contexto. Los contextos del navegador mantienen cookies y almacenamiento separados, de modo que el estado de un contexto no aparece en otro, y eso mantiene separadas las cuentas. El artículo sobre aislamiento de navegador para varias cuentas trata este modelo de aislamiento con más detalle, y las comprobaciones siguientes se apoyan en él.

Para quien revisa, la pregunta útil es qué estado existe al empezar y qué hace el recorrido cuando falta. Las cookies son la única capa que un equipo puede describir como un estado inicial repetible en el arranque, como se explica en gestión de cookies del navegador para flujos de varias identidades. localStorage e IndexedDB suelen empezar vacíos en un contexto nuevo y se llenan mientras la aplicación se ejecuta, así que una prueba que dependa de ellos debe crear ese estado mediante los flujos propios de la aplicación y registrar cómo lo hizo.

BotBrowser documenta la carga de cookies al iniciar con la opción --bot-cookies (nivel PRO), incluida la importación por contexto mediante botbrowserFlags, y documenta que cada BrowserContext tiene su propio almacenamiento, sus cookies y su estado de sesión, de modo que un equipo puede repetir un estado inicial de cookies documentado y mantener separadas las identidades. BotBrowser no documenta la precarga de localStorage ni de IndexedDB, no modifica las especificaciones de almacenamiento del navegador ni las reglas de cuota o expulsión, y no puede garantizar que un sitio de destino conserve o acepte ningún estado guardado. La documentación de gestión de cookies y la documentación de aislamiento multicuenta describen el comportamiento admitido.

Registra el resultado de una revisión en términos sencillos. Un buen registro nombra el mecanismo, su ámbito, el comportamiento esperado de caducidad o expulsión y lo que hace la aplicación cuando falta el estado, sin guardar contenido del usuario ni valores secretos. El registro puede repetirse tras una actualización mayor del navegador, un cambio en el código de almacenamiento o un cambio en los atributos de las cookies, y compararse con el último resultado aceptado. Un fallo debe nombrar el límite que se rompió, por ejemplo una cookie que no se envió o un depósito que se borró, y un responsable de la siguiente acción.

Los cambios en los atributos de las cookies merecen una repetición propia, porque los navegadores ajustan con el tiempo los valores por defecto de SameSite y el tratamiento de terceros. Compara el recorrido antes y después del cambio, y conserva la última configuración aceptada hasta que la nueva supere la prueba.

Ejecuta las comprobaciones de revisión del almacenamiento

Aplica estas comprobaciones a cada dato de estado que conserve un recorrido y registra aprobado o fallido en cada una.

  1. Para cada una de las cookies, localStorage, sessionStorage e IndexedDB que use el recorrido, el registro indica si se envía con las solicitudes HTTP. Aprueba si el panel de red del navegador muestra la cabecera Cookie en las solicitudes coincidentes y no muestra ningún valor de Web Storage ni de IndexedDB en ninguna solicitud. Falla si un valor que se suponía solo del cliente aparece en una solicitud.
  2. El registro nombra el ámbito de cada elemento: origen para Web Storage e IndexedDB, y host y ruta para las cookies. Abre la misma página en un segundo origen, por ejemplo otro puerto o subdominio, y confirma que los valores de localStorage e IndexedDB no son visibles allí. Falla si el registro dice solo «sitio» sin nombrar el límite que se aplica.
  3. Cierra la pestaña y vuelve a abrir la página, después reinicia el navegador, y registra cuáles de estos elementos permanecen tras cada paso: una cookie de sesión, una cookie persistente, un valor de localStorage, un valor de sessionStorage y un registro de IndexedDB. Anota la versión del navegador, porque la restauración de sesión y las políticas difieren. Aprueba si los elementos que permanecen coinciden con la columna de duración del registro para esa versión del navegador; falla ante cualquier discrepancia.
  4. Para un identificador de sesión, una preferencia pequeña y un dato estructurado sin conexión, el registro nombra el mecanismo elegido y la compensación de ciclo de vida que motivó la elección. Falla si un identificador de sesión está en un almacenamiento legible por scripts sin un motivo registrado.
  5. Borra un elemento con los controles de datos del sitio del navegador y recarga la página. Aprueba si la aplicación muestra su alternativa documentada, un valor por defecto, una nueva obtención o un aviso de inicio de sesión, sin ningún error sin gestionar. Falla si la página se rompe o si el código trata el valor ausente como información sobre quién es el usuario.
  6. Registra las respuestas de navigator.storage.persisted() y navigator.storage.estimate() como observaciones. Aprueba si la aplicación sigue funcionando cuando persisted() es false y los datos guardados se han eliminado. Falla si la aplicación da por supuesto que se concedió almacenamiento persistente.
  7. Repite las comprobaciones tras una actualización mayor del navegador, un cambio en el código de almacenamiento o un cambio en los atributos de las cookies. Conserva el último registro aceptado hasta que la repetición apruebe.

Fuentes

#Cookies#localStorage#IndexedDB#Datos Del Sitio#Almacenamiento Del Navegador

Lleva BotBrowser de la investigación a producción

Usa estas guías para entender el modelo y después avanzar hacia validación multiplataforma, contextos aislados y despliegue de navegador preparado para escalar.