Gli utenti premono il tasto indietro più spesso di quasi ogni altro controllo nel browser. Si aspettano che la schermata precedente appaia immediatamente, esattamente dove l'avevano lasciata. I browser moderni soddisfano questa aspettativa con la cache avanti/indietro, o bfcache. Invece di distruggere una pagina quando ci si allontana, il browser la congela in memoria. Quando si torna indietro, ne ripristina uno snapshot. Il browser salta l'analisi dell'HTML, la riesecuzione di JavaScript e il ricalcolo del layout. Il risultato sembra istantaneo perché la pagina non è mai morta del tutto.

Cosa fa effettivamente il bfcache

Il caricamento di una pagina normale è costoso. Il browser deve recuperare le risorse, tokenizzare l'HTML, costruire il DOM, eseguire gli script, risolvere gli stili, eseguire il layout, dipingere i pixel e comporre i livelli. Il bfcache aggira quasi tutto questo mantenendo la pagina in vita in uno stato congelato nella RAM. Non si tratta di una cache su disco. La pagina renderizzata, incluso l'heap di JavaScript, la posizione dello scroll e lo stato del modulo, risiede in memoria mentre l'utente legge la pagina successiva. Quando l'utente clicca indietro, il browser scongela lo snapshot e attiva un evento pageshow. La pagina riprende senza toccare la rete o rifare il layout da zero. Per gli utenti con dispositivi lenti o connessioni instabili, la differenza tra un ripristino tramite bfcache e un caricamento da zero può essere di centinaia di millisecondi o più.

Cosa lo impedisce

Un programmatore ha recentemente condotto un esperimento pulito per scoprire esattamente cosa blocca il bfcache. Ha creato sei pagine semplici, ognuna delle quali testava un potenziale blocco, per poi navigare altrove e premere indietro. I risultati sono stati chiari.

Una pagina di base, senza header o script insoliti, è stata ripristinata con successo. Anche una pagina con un listener beforeunload è stata ripristinata senza problemi. Sorprendentemente, anche una pagina servita con Cache-Control: no-store è entrata nel bfcache, contraddicendo le vecchie linee guida. Persino un articolo di un blog in tempo reale, che potrebbe sembrare troppo dinamico per essere congelato, è stato ripristinato con successo.

Due pagine sono fallite. Una pagina con un listener dell'evento unload non è stata ripristinata. Anche una pagina con una connessione WebSocket aperta è stata bloccata. Questi due fallimenti indicano le trappole che colpiscono i siti in produzione ogni giorno.

La trappola dell'evento unload

L'evento unload è da tempo il segnale di riferimento per la pulizia dell'ultimo secondo. Gli sviluppatori lo usano per inviare i beacon di analytics, terminare i timer o cancellare lo stato temporaneo. Il problema è che il bfcache si basa sull'idea che la pagina possa tornare in vita. Se il browser rileva un listener unload, assume che la pagina si aspetti una distruzione totale e rifiuta di congelarla. Non importa se la funzione collegata è vuota. La semplice presenza del listener è sufficiente a porre il veto alla cache in ogni browser moderno.

Il sostituto è pagehide. Questo evento viene attivato sia quando la pagina viene congelata per il bfcache, sia quando viene effettivamente scartata. Se è necessario distinguere tra i due, la proprietà event.persisted è true quando la pagina sta entrando nel bfcache. Per la maggior parte delle attività di pulizia, tuttavia, pagehide copre entrambi i percorsi. Sposta ogni logica di pulizia da unload a pagehide. Quindi rimuovi completamente ogni listener unload, inclusi quelli nascosti in snippet di analytics di terze parti o plugin legacy.

Trappole delle connessioni attive

Una connessione di rete o di archiviazione aperta segnala che la pagina sta ancora svolgendo un lavoro reale. Il browser inventaria le risorse attive al momento della navigazione. Se trova un WebSocket aperto, una connessione peer WebRTC attiva o una connessione IndexedDB residua, interrompe il congelamento e distrugge la pagina normalmente. Non ci si può fidare dello snapshot se i byte potrebbero ancora fluire.

Dovresti chiudere queste risorse all'interno di un listener pagehide. Chiama il metodo close del tuo WebSocket. Chiudi le connessioni peer WebRTC. Annulla o conferma qualsiasi transazione IndexedDB in sospeso. Se la tua app ha bisogno di quei canali quando l'utente torna, riaprili all'interno di pageshow. Questo pattern "close-on-pagehide, restore-on-pageshow" mantiene la pagina idonea per una navigazione indietro istantanea senza perdere funzionalità.

La sorpresa di no-store

Per anni, il senso comune ha sostenuto che Cache-Control: no-store impedisse il bfcache. Chrome ha cambiato questo comportamento nel 2025. Una pagina servita con no-store può ora entrare nel bfcache. Il browser espelle lo snapshot congelato solo in seguito, se gli stati di autenticazione o i cookie cambiano in modo tale da invalidare lo stato salvato. Se hai usato no-store come