Les utilisateurs appuient sur le bouton retour plus souvent que sur presque n'importe quel autre contrôle du navigateur. Ils s'attendent à ce que l'écran précédent apparaisse immédiatement, exactement là où ils l'ont laissé. Les navigateurs modernes répondent à cette attente grâce au cache de retour/avant, ou bfcache. Au lieu de détruire une page lorsque vous naviguez ailleurs, le navigateur la fige en mémoire. Lorsque vous revenez, il restaure un instantané. Le navigateur saute l'étape de l'analyse du HTML, de la réexécution du JavaScript et du recalcul de la mise en page. Le résultat donne une impression d'instantanéité car la page n'est jamais complètement morte.

Ce que fait réellement le bfcache

Le chargement d'une page normale est coûteux. Le navigateur doit récupérer les ressources, tokeniser le HTML, construire le DOM, exécuter les scripts, résoudre les styles, effectuer la mise en page, peindre les pixels et composer les couches. Le bfcache contourne presque tout cela en maintenant la page vivante dans un état figé en RAM. Il ne s'agit pas d'un cache disque. La page rendue, y compris le tas JavaScript (heap), la position de défilement et l'état du formulaire, réside en mémoire pendant que l'utilisateur lit la page suivante. Lorsque l'utilisateur clique sur retour, le navigateur décongèle l'instantané et déclenche un événement pageshow. La page reprend sans toucher au réseau ni refaire la mise en page à partir de zéro. Pour les utilisateurs sur des appareils lents ou des connexions instables, la différence entre une restauration par bfcache et un chargement frais peut se compter en centaines de millisecondes ou plus.

Ce qui le bloque

Un développeur a récemment mené une expérience rigoureuse pour découvrir exactement ce qui bloque le bfcache. Il a créé six pages simples, chacune testant un bloqueur suspecté, puis a navigué ailleurs avant d'appuyer sur retour. Les résultats étaient clairs.

Une page de référence sans en-têtes ou scripts inhabituels a été restaurée avec succès. Une page avec un écouteur beforeunload a également été restaurée sans problème. Étonnamment, une page servie avec Cache-Control: no-store est également entrée dans le bfcache, contredisant les anciennes recommandations. Même un article de blog en direct, qui pourrait sembler trop dynamique pour être figé, a été restauré avec succès.

Deux pages ont échoué. Une page avec un écouteur d'événement unload n'a pas pu être restaurée. Une page avec une connexion WebSocket ouverte a également été bloquée. Ces deux échecs pointent vers les pièges qui emprisonnent les sites de production chaque jour.

Le piège de l'événement unload

L'événement unload a longtemps été le signal privilégié pour le nettoyage de dernière seconde. Les développeurs l'utilisent pour envoyer des balises d'analyse (analytics beacons), arrêter des minuteurs ou effacer un état temporaire. Le problème est que le bfcache repose sur l'idée que la page pourrait revenir à la vie. Si le navigateur détecte un écouteur unload, il suppose que la page attend une destruction totale et refuse de la figer. Peu importe que la fonction attachée soit vide. La simple présence de l'écouteur suffit à mettre un veto au cache dans tous les navigateurs modernes.

Le remplaçant est pagehide. Cet événement se déclenche à la fois lorsque la page est en train d'être figée pour le bfcache et lorsqu'elle est réellement abandonnée. Si vous avez besoin de distinguer les deux, la propriété event.persisted est vraie lorsque la page se dirige vers le bfcache. Pour la plupart des tâches de nettoyage, cependant, pagehide couvre les deux cas. Déplacez toute la logique de nettoyage de unload vers pagehide. Ensuite, supprimez complètement chaque écouteur unload, y compris ceux cachés dans des extraits d'analyse tiers ou des plugins hérités.

Les pièges des connexions actives

Une connexion réseau ou de stockage ouverte signale que votre page effectue encore un travail réel. Le navigateur répertorie les ressources actives au moment de la navigation. S'il trouve un WebSocket ouvert, une connexion peer-to-peer WebRTC active ou une connexion IndexedDB persistante, il interrompt la congélation et démantèle la page normalement. On ne peut pas faire confiance à l'instantané tant que des octets pourraient encore circuler.

Vous devriez fermer ces ressources à l'intérieur d'un écouteur pagehide. Appelez la méthode close de votre WebSocket. Fermez les connexions peer-to-peer WebRTC. Annulez ou validez toutes les transactions IndexedDB en cours. Si votre application a besoin de ces canaux lorsque l'utilisateur revient, rouvrez-les à l'intérieur de pageshow. Ce modèle « close-on-pagehide, restore-on-pageshow » permet à la page d'être éligible à une navigation retour instantanée sans perdre de fonctionnalités.

La surprise du no-store

Pendant des années, la sagesse conventionnelle voulait que Cache-Control: no-store empêche le bfcache. Chrome a modifié ce comportement en 2025. Une page servie avec no-store peut désormais entrer dans le bfcache. Le navigateur n'évince l'instantané figé que plus tard si les états d'authentification ou les cookies changent d'une manière qui invalide l'état sauvegardé. Si vous avez utilisé no-store comme