Back/forward cache: perché il tasto Indietro del tuo sito è lento

La back/forward cache rende istantaneo il ritorno alla pagina precedente, ma molti siti ne sono esclusi senza saperlo: come funziona, cosa la blocca e come verificarla e gestirla.

Una finestra del browser con il tasto Indietro evidenziato in giallo e, accanto, una pila di pagine conservate in memoria che tornano sullo schermo

C’è un gesto che gli utenti compiono continuamente e che quasi nessuno ottimizza: il tasto Indietro. Si apre una scheda prodotto dall’elenco, si torna all’elenco; si legge un articolo, si torna alla home; si controlla una pagina di spedizioni, si torna al carrello. Su mobile, dove il gesto di scorrimento laterale o il tasto di sistema rendono il ritorno ancora più naturale, una parte consistente delle navigazioni è di questo tipo.

Tutti i browser moderni hanno un meccanismo per rendere questo gesto istantaneo: la back/forward cache, spesso abbreviata in bfcache. Quando funziona, la pagina precedente ricompare in pochi millisecondi, esattamente com’era, con la stessa posizione di scorrimento e gli stessi dati nei moduli. Quando non funziona, il browser ricarica tutto da capo. E su molti siti non funziona, per ragioni che spesso nessuno ha mai controllato.

Che cos’è la back/forward cache

La bfcache non è una cache HTTP e non ha nulla a che vedere con la cache di pagina del server. È una memoria del browser in cui viene conservata l’intera pagina appena lasciata, nel suo stato vivo: il DOM, lo stato del JavaScript, la posizione di scorrimento, i valori dei moduli. Quando l’utente se ne va, la pagina non viene distrutta ma congelata: i timer si fermano, gli script smettono di eseguire, ma tutto resta in memoria. Se l’utente torna indietro entro breve, la pagina viene scongelata e mostrata immediatamente, senza alcuna richiesta al server e senza eseguire di nuovo il codice.

La differenza con la cache HTTP è sostanziale. Anche con tutte le risorse in cache, una navigazione normale deve ricostruire la pagina: leggere l’HTML, applicare i fogli di stile, eseguire gli script, disegnare. Con la bfcache questo lavoro è già stato fatto e il risultato è semplicemente rimesso sullo schermo.

Confronto del tasto Indietro: senza bfcache il browser fa una nuova richiesta, scarica le risorse, esegue il JavaScript e ridisegna la pagina in 1,6 secondi perdendo scroll e moduli; con la bfcache la pagina viene ripristinata dalla memoria in 0,03 secondi con scroll e stato intatti
Con la bfcache il ritorno alla pagina precedente non richiede né rete né esecuzione di codice.

Perché conta per le Core Web Vitals e per il business

Le navigazioni all’indietro e in avanti non sono un caso marginale: secondo i dati pubblicati dal team di Chrome, rappresentano circa una navigazione su dieci da desktop e circa una su cinque da mobile. Il Chrome UX Report le conteggia come visite a tutti gli effetti, e distingue i tipi di navigazione, compresa la quota di ritorni serviti dalla bfcache.

Una pagina ripristinata dalla bfcache ha un LCP praticamente nullo, perché il contenuto è già visibile al primo fotogramma, e non produce spostamenti di layout legati al caricamento. Se una pagina ne è esclusa, ogni ritorno è un caricamento completo, con tutto ciò che comporta per LCP, CLS e INP. Rendere una pagina compatibile con la bfcache significa quindi migliorare i dati di campo su una fetta importante delle visite senza toccare server, immagini o script: si elimina del tutto il caricamento.

Sul piano dell’esperienza l’effetto è ancora più evidente. Pensiamo a un utente che scorre un lungo elenco di prodotti, apre una scheda e torna indietro: con la bfcache si ritrova esattamente nel punto in cui era, con i filtri applicati; senza, spesso riparte dall’inizio della pagina, deve ritrovare il punto e, se i filtri erano gestiti dal JavaScript, reimpostarli. È un attrito che gli utenti non sanno nominare ma percepiscono chiaramente, e che si traduce in sessioni più brevi e meno prodotti visti.

Perché una pagina viene esclusa

Il browser conserva una pagina nella bfcache solo se è sicuro poterla congelare e scongelare senza effetti indesiderati. Alcune caratteristiche della pagina, o delle risposte del server, lo impediscono. Le cause più frequenti sono poche, e quasi sempre non intenzionali.

Tabella dei motivi di esclusione dalla bfcache con i rimedi: Cache-Control no-store sull’HTML, da usare solo per pagine davvero private; listener dell’evento unload, da sostituire con pagehide o visibilitychange; WebSocket o WebRTC aperti, da chiudere in pagehide e riaprire in pageshow; transazioni IndexedDB in corso; window.opener verso un’altra pagina, da evitare con rel noopener; richieste fetch non concluse
Le cause più comuni di esclusione e il relativo rimedio.

Cache-Control: no-store

L’intestazione Cache-Control: no-store sul documento HTML dice al browser che la pagina non deve essere conservata in alcun modo. Per questo i browser hanno storicamente escluso dalla bfcache le pagine che la inviano. Ha senso per contenuti davvero sensibili, come l’area di un conto bancario, ma è frequente trovarla su pagine pubbliche per effetto di plugin, configurazioni del server o regole copiate da altri progetti. Chrome sta introducendo in modo progressivo la possibilità di usare la bfcache anche con no-store in condizioni sicure, per esempio quando i cookie non sono cambiati, ma non conviene contarci: l’intestazione va usata solo dove serve.

Va distinto il caso di no-cache, che chiede solo di riconvalidare la risorsa prima di riutilizzarla dalla cache HTTP e non impedisce la bfcache. Chi vuole evitare che una pagina venga servita dalla cache del browser senza controllo, ma non ha esigenze di riservatezza, dovrebbe usare no-cache o una durata breve con riconvalida, come abbiamo visto parlando di HTTP 304 Not Modified.

L’evento unload

L’evento unload viene emesso quando la pagina viene distrutta. Con la bfcache la pagina non viene distrutta, quindi il browser non può garantire che il codice associato venga eseguito: per non rompere quel codice, molti browser escludono direttamente le pagine che lo usano. Chrome ha avviato la dismissione graduale dell’evento, ma molti script di analisi, widget e plugin datati lo usano ancora. La sostituzione corretta è l’evento pagehide, o visibilitychange per inviare dati quando la pagina viene nascosta, che funzionano sia con sia senza bfcache.

// Da evitare: esclude la pagina dalla bfcache
window.addEventListener( 'unload', inviaStatistiche );

// Corretto: funziona anche quando la pagina viene congelata
document.addEventListener( 'visibilitychange', () => {
	if ( document.visibilityState === 'hidden' ) {
		inviaStatistiche();
	}
} );

Anche l’evento beforeunload va usato con parsimonia: ha senso per avvisare l’utente che sta per perdere un modulo non salvato, e in quel caso conviene aggiungerlo solo quando il modulo è stato modificato e rimuoverlo dopo il salvataggio, invece di tenerlo sempre attivo.

Connessioni aperte e altri casi

Una pagina con una connessione WebSocket o WebRTC aperta, tipica delle chat dal vivo e di alcuni widget di assistenza, non può essere congelata senza interrompere la connessione, e i browser tendono a escluderla. Lo stesso vale per transazioni IndexedDB in corso e, in alcuni browser, per richieste di rete non concluse. Anche una pagina aperta da un’altra con un riferimento window.opener attivo può essere esclusa: i link verso altre finestre dovrebbero usare rel="noopener", che i browser moderni applicano già per impostazione predefinita ai link con target="_blank".

La soluzione per le connessioni è chiuderle quando la pagina viene nascosta e riaprirle quando torna visibile, usando gli eventi pagehide e pageshow. Molti fornitori di chat e widget hanno aggiornato i propri script in questo senso; per gli altri, un caricamento differito al primo clic, come descritto nell’articolo sugli script di terze parti, evita il problema per tutte le visite in cui il widget non viene usato.

Come verificare se una pagina è compatibile

Il controllo più rapido si fa con i DevTools di Chrome: nel pannello Application, la sezione Back/forward cache contiene un pulsante che naviga altrove e torna indietro, e riporta se la pagina è stata ripristinata dalla bfcache oppure l’elenco preciso dei motivi per cui non lo è stata. È il primo test da fare su ogni modello di pagina importante: home, categoria, scheda prodotto, articolo, pagina di ricerca.

Anche Lighthouse include un controllo dedicato, che segnala le pagine che non possono usare la bfcache e i relativi motivi. Per conoscere la situazione sugli utenti reali esiste invece l’API notRestoredReasons, disponibile in Chrome, che espone nella voce di navigazione della Performance API i motivi per cui una navigazione all’indietro non è stata servita dalla bfcache, compresi quelli causati da iframe di terze parti:

const nav = performance.getEntriesByType( 'navigation' )[ 0 ];

if ( nav.type === 'back_forward' && nav.notRestoredReasons ) {
	// Esempio: [ { reason: 'unload-listener' }, ... ]
	inviaDiagnostica( nav.notRestoredReasons );
}

Raccolti per qualche giorno in un sistema di monitoraggio, questi dati indicano quali motivi pesano davvero sul traffico, e quindi da dove partire. Nei dati del Chrome UX Report, infine, la quota di navigazioni servite dalla bfcache sul totale dei ritorni è un indicatore semplice per seguire i progressi nel tempo.

Gestire correttamente il ripristino

Rendere una pagina compatibile con la bfcache significa anche accettare che possa tornare visibile senza essere ricaricata. Nella maggior parte dei casi è esattamente ciò che si vuole, ma alcuni contenuti vanno aggiornati: il contatore del carrello dopo aver aggiunto un prodotto in un’altra pagina, lo stato di accesso dopo un login, disponibilità e prezzi che cambiano rapidamente. L’evento pageshow, con la proprietà persisted, permette di riconoscere un ripristino e di aggiornare solo ciò che serve:

window.addEventListener( 'pageshow', ( event ) => {
	if ( event.persisted ) {
		// La pagina arriva dalla bfcache: aggiorna i dati volatili
		aggiornaContatoreCarrello();
		inviaVisualizzazione();
	}
} );

Lo stesso evento è il punto giusto per l’analytics. Un ripristino dalla bfcache è una visualizzazione di pagina a tutti gli effetti, ma poiché gli script non vengono rieseguiti, un sistema che registra le visite solo al caricamento non la vede. Le principali piattaforme di analisi gestiscono già questo caso; il codice personalizzato va verificato.

Per le pagine con dati davvero sensibili, invece, l’esclusione è la scelta corretta. Un’area riservata che mostra informazioni personali non dovrebbe ricomparire con il tasto Indietro dopo il logout, e per queste pagine Cache-Control: no-store resta lo strumento appropriato. La regola è semplice: escludere consapevolmente ciò che è privato, rendere compatibile tutto il resto.

Il caso WordPress

Sui siti WordPress le pagine pubbliche sono in genere compatibili con la bfcache, ma alcune situazioni ricorrenti le escludono. Plugin di sicurezza o di cache configurati per inviare no-store a tutte le pagine; plugin di consenso ai cookie, statistiche o popup che usano ancora l’evento unload; widget di chat con connessioni sempre aperte; negozi che inviano intestazioni restrittive su tutte le pagine anziché solo su carrello, checkout e area cliente. Per gli utenti collegati WordPress invia intestazioni di non memorizzazione, ed è normale che l’area amministrativa sia esclusa.

La verifica è rapida: si controllano le intestazioni della risposta HTML delle pagine pubbliche, si esegue il test dei DevTools sui modelli principali e si identifica lo script responsabile di ogni motivo segnalato. Spesso la correzione è un’impostazione di un plugin, un aggiornamento o la sostituzione di uno script obsoleto.

Bfcache e Speculation Rules: le due metà della navigazione

Nell’articolo precedente abbiamo visto come le Speculation Rules rendano istantanea la navigazione verso pagine nuove, caricandole prima del clic. La bfcache fa lo stesso per le pagine già visitate. Insieme coprono gran parte della navigazione interna di un sito: in avanti con il prerender, all’indietro con la memoria del browser. E condividono la stessa disciplina: il codice non deve dare per scontato che una pagina venga caricata una sola volta e vista subito, ma reagire agli eventi prerenderingchange e pageshow.

In sintesi

La back/forward cache rende istantaneo il ritorno alle pagine già visitate, conservandole in memoria nel loro stato completo. Riguarda una parte importante delle navigazioni, soprattutto da mobile, e porta a zero il tempo di caricamento per quelle visite, con effetti diretti sulle Core Web Vitals misurate sul campo e sull’esperienza di chi naviga tra elenchi e dettagli.

Il lavoro consiste soprattutto nel rimuovere gli ostacoli: no-store dove non serve, listener unload, connessioni lasciate aperte. Si verifica con i DevTools e con Lighthouse, si misura sugli utenti reali con notRestoredReasons, e si gestisce il ripristino con l’evento pageshow per aggiornare i dati volatili e l’analytics. È uno degli interventi con il miglior rapporto tra impegno e risultato: non rende più leggero nulla, semplicemente evita di rifare un lavoro già fatto.

IL PROSSIMO PASSO

Partiamo
dal tuo sito.

Raccontaci il progetto, i problemi che riscontri e i tuoi obiettivi. Definiremo insieme un intervento su misura.

Richiedi un’analisi

Ti porta al modulo della pagina Contatti.

MANAGED SERVER S.R.L.info@corewebvitals.it +39 02 5656 9681

Lunedì – venerdì · 09:30 – 19:30

Preferisci un modulo online?
Contattaci su managedserver.it ↗