Retour au Blog
Identité

Cookies, localStorage et IndexedDB : où ranger l’état

Comparez les cookies, Web Storage et IndexedDB selon la portée, l’exposition réseau, la capacité et l’éviction, puis choisissez où chaque état doit vivre.

Documentation

Vous voulez la documentation structurée pour Identité ?

Cet article fait partie de la bibliothèque éditoriale. Pour les étapes de configuration, la référence et les mises à jour continues, passez directement à la section docs.

Ce que contient chaque mécanisme de stockage

Une page dispose de quatre emplacements courants pour conserver un état, et ils diffèrent par la personne qui peut lire les données, le moment où elles circulent sur le réseau et leur durée de vie. Les cookies sont de petites paires nom-valeur que le serveur comme la page peuvent définir. Web Storage comporte deux parties, localStorage et sessionStorage, et conserve des paires clé-valeur de type texte pour les scripts. IndexedDB est une base de données asynchrone et transactionnelle pour les données structurées. Choisir entre ces mécanismes est une décision de cycle de vie et d’exposition, pas une question d’habitude.

Comparaison des cookies, de localStorage, de sessionStorage et d’IndexedDB selon l’exposition réseau, la portée, la durée de vie et l’éviction, avec un espace de stockage à persistance au mieux

Les cookies sont définis par la RFC 6265. Un serveur en crée un avec un en-tête de réponse Set-Cookie, ou un script en crée un via document.cookie, et le navigateur joint les cookies correspondants aux requêtes suivantes dans un en-tête Cookie. Des attributs comme Domain, Path, Secure et HttpOnly, ainsi que l’attribut SameSite plus récent décrit par MDN, restreignent l’endroit où un cookie est envoyé et la personne qui peut le lire. Un cookie marqué HttpOnly ne peut pas être lu par le script de la page, ce qui le rend adapté à un identifiant de session émis par le serveur. Le guide MDN sur les cookies décrit le comportement de ces attributs dans les navigateurs actuels.

Web Storage est spécifié dans le standard HTML. localStorage et sessionStorage exposent la même interface synchrone, avec getItem, setItem, removeItem et clear, pour des clés et des valeurs de type texte. Tout ce qui est plus riche, comme un objet ou une liste, doit être sérialisé par l’application. Comme les appels sont synchrones, de grandes lectures et écritures sur le fil principal peuvent retarder le rendu, si bien que Web Storage convient aux petites valeurs.

IndexedDB est spécifié par le W3C. Elle stocke des valeurs structurées, y compris des fichiers et des blobs, dans des magasins d’objets pouvant avoir des index, et lit et écrit par des requêtes asynchrones à l’intérieur de transactions. Une base de données porte un numéro de version, et un changement de schéma s’exécute dans une étape de mise à niveau que l’application contrôle. Elle est aussi disponible dans les workers, de sorte que les lectures lourdes n’ont pas à occuper le fil principal. Le prix à payer est plus de code et plus d’états à gérer qu’avec un appel d’une ligne à localStorage.

Le stockage par origine couvre aussi d’autres magasins, comme l’API Cache et les enregistrements de service workers, qui coexistent avec ces mécanismes et sont soumis aux mêmes règles de quota et d’éviction dans les navigateurs qui implémentent le Storage Standard. Un nettoyage ou une éviction peut donc supprimer plus que les magasins comparés ici, et la personne qui relit ne doit pas supposer qu’une valeur d’un magasin survit aux autres.

Portée et exposition réseau

La portée est l’endroit où les mécanismes diffèrent le plus. Web Storage et IndexedDB ont pour portée une origine, c’est-à-dire la combinaison du schéma, de l’hôte et du port, de sorte que https://example.com et https://example.com:8443 conservent des données distinctes. Les cookies ont plutôt pour portée un hôte et un chemin, et la spécification des cookies ne les sépare pas par port ; l’attribut Secure est le seul contrôle lié au schéma. SameSite ajoute une notion distincte de site, un domaine enregistrable, qui détermine si un cookie accompagne les requêtes intersites. Gardez l’origine et le site distincts lorsque vous raisonnez sur le code qui peut voir une valeur.

L’exposition réseau en découle. Parmi les quatre mécanismes, seuls les cookies sont joints automatiquement aux requêtes HTTP, si bien que chaque requête correspondante les transporte, que le serveur ait besoin de la valeur ou non. C’est utile pour un identifiant de session que le serveur doit lire à chaque requête, et coûteux pour toute donnée volumineuse, car les octets voyagent avec chaque requête vers cet hôte. localStorage, sessionStorage et IndexedDB ne quittent jamais le navigateur, sauf si le code de l’application lit une valeur et l’envoie. Cette différence détermine aussi quel côté peut agir : un serveur peut lire un cookie sans exécuter aucun script, mais il ne peut pas voir du tout Web Storage ni IndexedDB.

L’exposition aux scripts est l’image inverse. Tout script qui s’exécute dans une origine peut lire le localStorage, le sessionStorage et l’IndexedDB de cette origine, y compris les scripts tiers que la page inclut et tout script injecté par une faille de type cross-site scripting. Un cookie marqué HttpOnly est caché aux scripts, c’est donc l’endroit le plus sûr pour un identifiant d’accès. Un jeton porteur conservé dans localStorage est lisible par le code même qui affiche la page. Consignez cela comme un compromis de conception ; ce n’est pas une raison d’éviter Web Storage pour des préférences ordinaires.

La taille des cookies mérite une remarque à part. Comme les cookies voyagent avec les requêtes, un ensemble de cookies qui grossit alourdit chaque requête, et les navigateurs imposent leurs propres limites sur la taille et le nombre de cookies par hôte. La RFC 6265 demande aux agents utilisateurs de ne prendre en charge que des minimums modestes ; un cookie doit donc porter un identifiant ou un court indicateur et laisser les données plus volumineuses à un enregistrement côté serveur ou au stockage côté client.

Les contextes intégrés et tiers ajoutent une couche supplémentaire. Les navigateurs partitionnent de plus en plus le stockage et les cookies selon le site de premier niveau, si bien qu’un cadre intégré peut voir un espace différent de celui que voit la même origine en tant que page de premier niveau. Les détails varient selon le navigateur et la version. L’article sur le partitionnement du stockage du navigateur et la vie privée explique comment le tester. Lorsqu’une fonctionnalité dépend d’un état à l’intérieur d’un cadre intégré, testez-la dans cette position intégrée et ne supposez pas que le résultat du premier niveau s’y applique.

Durée de vie, capacité et éviction

La durée de vie a deux bornes : le moment où l’état cesse d’être disponible par conception, et le moment où le navigateur le supprime. Les cookies de session, ceux sans Expires ni Max-Age, durent jusqu’à la fin de la session du navigateur, une limite que le navigateur définit, et certains navigateurs restaurent les sessions et les conservent après un redémarrage. Les cookies persistants durent jusqu’à leur date d’expiration, ou jusqu’à ce que l’utilisateur ou le navigateur les supprime. localStorage et IndexedDB n’ont pas d’expiration propre et subsistent jusqu’à ce qu’un script, l’utilisateur ou le navigateur les efface. sessionStorage dure autant que son contexte de navigation de premier niveau, à peu près un onglet, et survit aux rechargements mais pas à la fermeture de l’onglet.

La fermeture des éléments montre bien la différence. Fermer un onglet met fin au sessionStorage de cet onglet, mais laisse en place un cookie de session, localStorage et IndexedDB. Fermer tout le navigateur met en général fin aux cookies de session, même si la restauration de session peut les ramener, et laisse localStorage et IndexedDB. Un deuxième onglet ouvert indépendamment sur la même origine partage les cookies, localStorage et IndexedDB, mais dispose de son propre sessionStorage (une fenêtre ouverte par script démarre avec une copie). Un événement storage prévient aussi les autres documents de la même origine lorsque localStorage change, ce qui permet aux onglets de rester synchronisés.

La capacité diffère elle aussi. Les cookies sont limités à de petites valeurs et à un nombre limité par hôte. Web Storage autorise couramment quelques mégaoctets par origine, et IndexedDB beaucoup plus, dans la limite d’un quota que le navigateur déduit de la taille totale du disque. Ces chiffres dépendent du navigateur, et la page MDN sur les quotas de stockage et les critères d’éviction le dit clairement. Une application doit lire ses limites à l’exécution et gérer une erreur de quota, plutôt que de figer une hypothèse tirée d’un seul navigateur.

Le Storage Standard ajoute la règle la plus importante pour la conception : la persistance est au mieux par défaut. Chaque origine possède un espace de stockage, et selon le Storage Standard le navigateur peut effacer un espace à persistance au mieux lorsqu’il a besoin de place, en supprimant les données de l’origine comme un tout et sans promesse de demander d’abord. Une application peut appeler navigator.storage.persist() pour demander un stockage persistant, et le navigateur décide de l’accorder selon une politique qui lui est propre et qui peut passer par une invite à l’utilisateur. navigator.storage.estimate() indique une utilisation et un quota approximatifs, et navigator.storage.persisted() indique si l’espace est persistant. Considérez les trois comme des indications, pas comme des garanties.

L’éviction n’est pas la seule façon dont un état disparaît. Les utilisateurs effacent les données de site, les modes privés suppriment le stockage à la fermeture de la fenêtre, un site peut envoyer un en-tête de réponse Clear-Site-Data pour demander au navigateur d’effacer les cookies ou le stockage de sa propre origine, et une mise à jour du navigateur ou un changement de profil peut réinitialiser ce qu’un profil contient. Les politiques évoluent aussi d’une version à l’autre, de sorte qu’un comportement observé une fois n’est pas une promesse. Les spécifications et MDN décrivent ces comportements comme dépendants du navigateur, et rien ici ne promet des quotas, une expiration ou une éviction identiques selon les navigateurs, les versions ou les modes privés.

Les données stockées survivent aussi au code qui les a écrites. Lorsqu’une version change la forme d’une valeur stockée, les anciennes entrées doivent être lues, migrées ou écartées, et un changement de schéma IndexedDB exige une augmentation de version et une étape de mise à niveau. Un champ de version dans les valeurs de localStorage remplit le même rôle. Une équipe qui saute cette étape la découvre la première fois qu’un navigateur de retour charge des données écrites par une version plus ancienne.

Comme la persistance est au mieux, l’application doit traiter le stockage du navigateur comme un cache d’état qu’elle peut reconstruire, sauf si les données sont l’unique copie et que l’utilisateur en a été informé. Lorsqu’une valeur manque, l’application doit se rabattre sur une valeur par défaut documentée, récupérer à nouveau l’état auprès du serveur, ou demander à l’utilisateur de se connecter ou de ressaisir un brouillon. Entourez les lectures et les écritures d’une gestion d’erreurs, car une écriture peut lever une erreur de quota et certains contextes refusent entièrement le stockage, et assurez-vous que le parcours de première utilisation fonctionne avec un magasin vide.

Choisir un mécanisme pour chaque état

Partez de l’état, pas de l’API. Pour un identifiant de session que le serveur doit lire à chaque requête, utilisez un cookie avec Secure, HttpOnly et une valeur SameSite appropriée, ainsi qu’une durée de vie que le serveur peut imposer et révoquer. Deux compromis guident ce choix : l’exposition réseau et l’exposition aux scripts. La transmission automatique et la protection contre la lecture par les scripts l’emportent sur la limite de taille, car un identifiant est minuscule.

Pour une petite préférence comme un thème, une langue ou un avis fermé, localStorage suffit généralement. La valeur est une courte chaîne, seul le code côté client en a besoin, et la perdre coûte un clic à l’utilisateur. Si le serveur doit produire la première réponse avec cette préférence, un cookie est un meilleur emplacement parce que le serveur peut le voir ; sinon, évitez de mettre les préférences dans chaque requête. Utilisez sessionStorage lorsque la valeur doit prendre fin avec l’onglet, par exemple une étape de formulaire inachevée qui ne doit pas apparaître dans un autre onglet.

Pour des données structurées hors connexion, comme une file de modifications non envoyées, des enregistrements en cache ou des fichiers, utilisez IndexedDB. Elle gère de plus gros volumes, des index et des transactions, et fonctionne depuis les workers. Son prix est la règle de la persistance au mieux : une application qui y stocke l’unique copie d’une modification non envoyée s’appuie sur un espace que le navigateur peut évincer. Signalez ces données comme en attente dans l’interface, synchronisez-les avec le serveur dès que possible et ne demandez un stockage persistant que lorsque les données le justifient.

Une conception mixte est normale et souvent correcte. Un produit peut garder un cookie de session HttpOnly, une préférence de thème dans localStorage et une file hors connexion dans IndexedDB, chacun choisi pour son cycle de vie. Ce que la conception doit éviter, c’est de dupliquer la même valeur à plusieurs endroits sans règle indiquant quelle copie l’emporte, car les copies divergent lorsque l’une est évincée ou effacée et pas les autres. Désignez un seul responsable pour chaque état et traitez les autres copies comme dérivées.

Une valeur absente demande une lecture attentive. Un cookie absent ou un localStorage vide indique seulement à une application que ce navigateur n’a pas conservé cette valeur ou ne l’a jamais reçue. Cela ne dit rien de fiable sur l’identité de l’utilisateur, sur le fait que la visite soit la première, ni sur le type de client en cours d’exécution ; il ne faut donc pas en faire un jugement d’identité ou de confiance. Servez-vous-en pour décider quoi afficher ou reconstruire, et laissez à l’authentification les décisions concernant les personnes. Cette comparaison sert à concevoir et à relire le stockage de votre propre application ; lire, copier ou remplacer l’état stocké par un autre site n’entre pas dans son périmètre.

Relire le stockage dans un flux à plusieurs contextes

Les équipes qui exécutent le même parcours dans plusieurs contextes de navigateur doivent savoir avec quel état chaque contexte démarre. Les contextes de navigateur conservent des cookies et un stockage séparés, de sorte que l’état d’un contexte n’apparaît pas dans un autre, ce qui garde les comptes distincts. L’article sur l’isolation de navigateur pour plusieurs comptes traite ce modèle d’isolation plus en détail, et les vérifications ci-dessous s’appuient sur lui.

Pour la personne qui relit, la question utile est de savoir quel état existe au départ et ce que fait le parcours lorsqu’il est absent. Les cookies sont la seule couche qu’une équipe puisse décrire comme un état de départ reproductible au lancement, comme l’explique l’article sur la gestion des cookies du navigateur pour les flux multi-identités. localStorage et IndexedDB démarrent normalement vides dans un contexte neuf et se remplissent à mesure que l’application s’exécute ; un test qui en dépend doit donc créer cet état par les parcours propres de l’application et consigner la façon dont il l’a fait.

BotBrowser documente le chargement de cookies au lancement avec l’option --bot-cookies (niveau PRO), y compris l’import par contexte via botbrowserFlags, et documente que chaque BrowserContext dispose de son propre stockage, de ses propres cookies et de son propre état de session, de sorte qu’une équipe peut répéter un état de départ des cookies documenté et garder les identités séparées. BotBrowser ne documente pas le préchargement de localStorage ni d’IndexedDB, ne modifie pas les spécifications de stockage du navigateur ni les règles de quota ou d’éviction, et ne peut pas garantir qu’un site cible conserve ou accepte un quelconque état stocké. La documentation sur la gestion des cookies et la documentation sur l’isolation multicompte décrivent le comportement pris en charge.

Consignez le résultat d’une relecture en termes simples. Un bon enregistrement nomme le mécanisme, sa portée, le comportement attendu d’expiration ou d’éviction et ce que fait l’application lorsque l’état est absent, sans conserver de contenu d’utilisateur ni de valeurs secrètes. L’enregistrement peut être répété après une mise à jour majeure du navigateur, un changement du code de stockage ou un changement des attributs des cookies, et comparé au dernier résultat accepté. Un échec doit nommer la limite qui a cédé, par exemple un cookie qui n’a pas été envoyé ou un espace qui a été effacé, et un responsable de l’action suivante.

Les changements d’attributs des cookies méritent leur propre répétition, car les navigateurs ajustent avec le temps les valeurs par défaut de SameSite et le traitement des tiers. Comparez le parcours avant et après le changement, et conservez la dernière configuration acceptée jusqu’à ce que la nouvelle réussisse.

Exécuter les vérifications de relecture du stockage

Appliquez ces vérifications à chaque état qu’un parcours conserve, et consignez réussite ou échec pour chacune.

  1. Pour chacun des cookies, localStorage, sessionStorage et IndexedDB que le parcours utilise, l’enregistrement indique s’il est envoyé avec les requêtes HTTP. Réussite si le panneau réseau du navigateur montre l’en-tête Cookie sur les requêtes correspondantes et ne montre aucune valeur de Web Storage ni d’IndexedDB dans aucune requête. Échec si une valeur supposée rester côté client apparaît dans une requête.
  2. L’enregistrement nomme la portée de chaque élément : l’origine pour Web Storage et IndexedDB, l’hôte et le chemin pour les cookies. Ouvrez la même page sur une seconde origine, par exemple un autre port ou un sous-domaine, et confirmez que les valeurs de localStorage et d’IndexedDB n’y sont pas visibles. Échec si l’enregistrement dit seulement « site » sans nommer la limite qui s’applique.
  3. Fermez l’onglet et rouvrez la page, puis redémarrez le navigateur, et consignez lesquels de ces éléments subsistent après chaque étape : un cookie de session, un cookie persistant, une valeur de localStorage, une valeur de sessionStorage et un enregistrement IndexedDB. Notez la version du navigateur, car la restauration de session et les politiques diffèrent. Réussite si les éléments qui subsistent correspondent à la colonne de durée de vie de l’enregistrement pour cette version du navigateur ; échec en cas d’écart.
  4. Pour un identifiant de session, une petite préférence et une donnée structurée hors connexion, l’enregistrement nomme le mécanisme choisi et le compromis de cycle de vie qui a motivé le choix. Échec si un identifiant de session se trouve dans un stockage lisible par les scripts sans raison consignée.
  5. Effacez un élément avec les contrôles de données de site du navigateur et rechargez la page. Réussite si l’application affiche sa solution de repli documentée, une valeur par défaut, une nouvelle récupération ou une invite de connexion, sans erreur non gérée. Échec si la page se casse, ou si le code traite la valeur absente comme une information sur l’identité de l’utilisateur.
  6. Consignez les réponses de navigator.storage.persisted() et de navigator.storage.estimate() comme des observations. Réussite si l’application fonctionne encore lorsque persisted() vaut false et que les données stockées ont été supprimées. Échec si l’application suppose qu’un stockage persistant a été accordé.
  7. Répétez les vérifications après une mise à jour majeure du navigateur, un changement du code de stockage ou un changement des attributs des cookies. Conservez le dernier enregistrement accepté jusqu’à ce que la répétition réussisse.

Sources

#Cookies#localStorage#IndexedDB#Données De Site#Stockage Du Navigateur

Faites passer BotBrowser de la recherche à la production

Utilisez ces guides pour comprendre le modèle, puis passez à la validation multi-plateforme, aux contextes isolés et au déploiement navigateur prêt pour l'échelle.