Cache back-forward

La cache back-forward (o bfcache) è un'ottimizzazione del browser che consente una navigazione istantanea in avanti e indietro. Migliora notevolmente l'esperienza di navigazione, soprattutto per gli utenti con reti o dispositivi più lenti.

Questa pagina descrive come ottimizzare le pagine per la cache back-forward in tutti i browser.

Compatibilità del browser

bfcache è supportata da Firefox e Safari da molti anni, sia su computer che su dispositivi mobili.

A partire dalla versione 86, Chrome abilitava la cache back-forward per le navigazioni tra siti su Android per una piccola percentuale di utenti. Nelle release successive è stato gradualmente implementato supporto aggiuntivo. Dalla versione 96, la cache bfcache è attiva per tutti gli utenti di Chrome su computer e dispositivi mobili.

nozioni di base su bfcache

bfcache è una cache in memoria che archivia un'istantanea completa di una pagina mentre l'utente esce dalla pagina. Con l'intera pagina in memoria, il browser può ripristinarla rapidamente se l'utente decide di tornare, invece di dover ripetere tutte le richieste di rete necessarie per caricare la pagina.

Il seguente video mostra in che misura la cache back-forward può velocizzare la navigazione:

L'uso della cache back-forward velocizza il caricamento delle pagine durante la navigazione in avanti e indietro.

I dati sull'utilizzo di Chrome indicano che 1 navigazione su 10 su computer e 1 su 5 su dispositivi mobili è andata indietro o avanti. Per questo motivo, la cache back-forward consente di risparmiare molto tempo e utilizzare i dati.

Come funziona la "cache"

La "cache" utilizzata da bfcache è diversa dalla cache HTTP, che svolge il proprio ruolo per velocizzare le navigazioni ripetute. bfcache è un'istantanea dell'intera pagina in memoria, incluso l'heap JavaScript, mentre la cache HTTP contiene solo le risposte per le richieste effettuate in precedenza. Poiché è molto raro che tutte le richieste necessarie per caricare una pagina siano fulfullable dalla cache HTTP, le visite ripetute utilizzando i ripristini di bfcache sono sempre più veloci delle navigazioni con migliore ottimizzazione.

La creazione di uno snapshot di una pagina in memoria, tuttavia, comporta un certo grado di complessità in termini di conservazione del codice in corso. Ad esempio, come gestisci le chiamate setTimeout() in cui viene raggiunto il timeout mentre la pagina si trova nella cache back-forward?

La risposta è che i browser mettono in pausa eventuali timer in sospeso o promesse non risolte per le pagine nella cache back-forward, incluse quasi tutte le attività in sospeso nelle coe di attività JavaScript, e ripristinano le attività di elaborazione se la pagina viene ripristinata dalla cache back-forward.

In alcuni casi, ad esempio per timeout e promesse, questo rischio è piuttosto basso, ma in altri casi può portare a comportamenti confusi o imprevisti. Ad esempio, se il browser mette in pausa un'attività richiesta per una transazione IndexedDB, potrebbe influire su altre schede aperte nella stessa origine, perché più schede possono accedere contemporaneamente agli stessi database di IndexedDB. Di conseguenza, solitamente i browser non tentano di memorizzare nella cache le pagine nel mezzo di una transazione IndexedDB o durante l'utilizzo di API che potrebbero influire su altre pagine.

Per ulteriori dettagli su come l'utilizzo dell'API influisce sull'idoneità alla cache back-forward di una pagina, consulta Ottimizzare le pagine per la cache back-forward.

bfcache e app a pagina singola (SPA)

Poiché bfcache funziona con le navigazioni gestite dal browser, non funziona per le "navigazioni soft" all'interno di un'app a pagina singola (SPA). Tuttavia, bfcache può comunque essere utile quando si esce e si torna da una SPA.

API per osservare la cache back-forward

Sebbene bfcache sia un'ottimizzazione che i browser eseguono automaticamente, è comunque importante che gli sviluppatori sappiano quando viene eseguita, in modo da ottimizzare le pagine e regolare eventuali metriche o misurazioni delle prestazioni di conseguenza.

Gli eventi principali utilizzati per osservare la cache back-forward sono gli eventi di transizione delle pagine pageshow e pagehide, supportati dalla maggior parte dei browser.

I più recenti eventi Ciclo di vita della pagina, freeze e resume, vengono inviati anche quando le pagine entrano o escono dalla cache back-forward, nonché in altre situazioni, ad esempio, quando una scheda in background viene bloccata per ridurre al minimo l'utilizzo della CPU. Questi eventi sono supportati solo nei browser basati su Chromium.

Osserva quando una pagina viene ripristinata dalla cache back-forward

L'evento pageshow viene attivato subito dopo l'evento load durante il caricamento iniziale della pagina e ogni volta che viene ripristinata dalla cache back-forward. L'evento pageshow ha una proprietà persisted, ovvero true se la pagina è stata ripristinata da bfcache e false in caso contrario. Puoi usare la proprietà persisted per distinguere i normali caricamenti di pagina dai ripristini della cache back-forward. Ad esempio:

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    console.log('This page was restored from the bfcache.');
  } else {
    console.log('This page was loaded normally.');
  }
});

Nei browser che supportano l'API Page Lifecycle, l'evento resume si attiva quando le pagine vengono ripristinate dalla cache back-forward (immediatamente prima dell'evento pageshow) e quando un utente accede di nuovo a una scheda in background bloccata. Se vuoi aggiornare lo stato di una pagina dopo che è stata bloccata (che include pagine nella cache back-forward), puoi utilizzare l'evento resume, ma se vuoi misurare la percentuale di hit della cache bfcache del tuo sito devi utilizzare l'evento pageshow. In alcuni casi, potresti dover utilizzare entrambi.

Per maggiori dettagli sulle best practice per la misurazione della cache back-forward, consulta In che modo la cache back-forward influisce sull'analisi e sulla misurazione delle prestazioni.

Osserva quando una pagina inserisce bfcache

L'evento pagehide viene attivato durante l'unload di una pagina o quando il browser tenta di inserirlo nella cache back-forward.

L'evento pagehide ha anche una proprietà persisted. Se è false, puoi avere la certezza che la pagina non verrà inserita nella cache back-forward. Tuttavia, il fatto che persisted sia true non garantisce che una pagina venga memorizzata nella cache. Significa che il browser intends a memorizzare la pagina nella cache, ma potrebbero esserci altri fattori che ne impediscono la memorizzazione.

window.addEventListener('pagehide', (event) => {
  if (event.persisted) {
    console.log('This page *might* be entering the bfcache.');
  } else {
    console.log('This page will unload normally and be discarded.');
  }
});

Analogamente, l'evento freeze viene attivato subito dopo l'evento pagehide se persisted è true, ma questo significa solo che il browser intends a memorizzare la pagina nella cache. Potrebbe essere necessario eliminarlo per una serie di motivi spiegati di seguito.

Ottimizza le tue pagine per la cache back-forward

Non tutte le pagine vengono memorizzate nella cache back-forward e, anche se una pagina viene archiviata al suo interno, non ci rimangono per sempre. Le pagine seguenti descrivono ciò che rende le pagine idonee alla cache back-forward e consiglia best practice per massimizzare la capacità del browser di memorizzare la pagina nella cache per ottenere migliori percentuali di hit.

Usa pagehide anziché unload

Il modo più importante per ottimizzare la cache back-forward in tutti i browser è non utilizzare mai i listener di eventi unload. Ascolta invece pagehide, perché si attiva sia quando una pagina entra nella cache bfcache che ogni volta che viene attivato unload.

unload è una funzionalità meno recente, originariamente progettata per essere attivata ogni volta che un utente abbandona una pagina. Non è più così, ma molte pagine web continuano a funzionare partendo dal presupposto che i browser utilizzino unload in questo modo e che, dopo l'attivazione unload, la pagina non caricata smetta di esistere. Questo potrebbe causare l'interruzione della cache back-forward se il browser tenta di memorizzare nella cache una pagina non caricata.

Sui computer, Chrome e Firefox rendono le pagine con listener unload non idonee per la cache back-forward, il che riduce i rischi, ma comporta anche la mancata memorizzazione nella cache di molte pagine, con conseguente ricaricamento molto più lento. Safari cerca di memorizzare nella cache alcune pagine con i listener di eventi unload, ma per ridurre potenziali interruzioni, non esegue l'evento unload quando un utente esce dalla pagina, il che rende i listener unload inaffidabili.

Sui dispositivi mobili, Chrome e Safari provano a memorizzare nella cache le pagine con listener di eventi unload, perché l'inaffidabilità di unload sui dispositivi mobili riduce il rischio di interruzione. Firefox mobile tratta le pagine che utilizzano unload come non idonee per la cache back-forward, tranne che su iOS, che richiede a tutti i browser di utilizzare il motore di rendering WebKit, quindi si comporta come Safari.

Per determinare se un codice JavaScript sulle tue pagine utilizza unload, ti consigliamo di utilizzare il controllo no-unload-listeners in Lighthouse.

Per informazioni sul piano di Chrome per ritirare unload, consulta la sezione Ritirare l'evento unload.

Utilizza le norme di autorizzazione per impedire l'utilizzo dei gestori dell'unload in una pagina

Alcuni script ed estensioni di terze parti possono aggiungere gestori dell'unload a una pagina, rallentando il sito rendendolo non idoneo per la cache back-forward. Per impedire che questo accada in Chrome 115 e versioni successive, utilizza un criterio di autorizzazione.

Permission-Policy: unload()

Aggiungi solo beforeunload listener in modo condizionale

L'evento beforeunload non rende le tue pagine non idonee per la cache back-forward. Tuttavia, non è affidabile, quindi ti consigliamo di utilizzarlo solo quando è assolutamente necessario.

Un esempio di caso d'uso per beforeunload consiste nell'avviso a un utente che ha modifiche non salvate che andranno perse se lasciano la pagina. In questo caso, ti consigliamo di aggiungere listener beforeunload solo quando per un utente sono presenti modifiche non salvate e di rimuoverli subito dopo il salvataggio delle modifiche non salvate, come indicato nel seguente codice:

function beforeUnloadListener(event) {
  event.preventDefault();
  return event.returnValue = 'Are you sure you want to exit?';
};

// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  window.addEventListener('beforeunload', beforeUnloadListener);
});

// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  window.removeEventListener('beforeunload', beforeUnloadListener);
});

Riduci al minimo l'utilizzo di Cache-Control: no-store

Cache-Control: no-store è un'intestazione HTTP che i server web possono impostare sulle risposte per indicare al browser di non memorizzare la risposta in alcuna cache HTTP. Viene utilizzato per le risorse contenenti informazioni sensibili sugli utenti, ad esempio le pagine protette da un accesso.

Sebbene bfcache non sia una cache HTTP, i browser hanno storicamente escluso le pagine da bfcache quando è impostato Cache-Control: no-store nella risorsa della pagina (ma non in nessuna risorsa secondaria). Chrome sta cercando di modificare questo comportamento rispettando la privacy degli utenti, ma per impostazione predefinita le pagine che utilizzano Cache-Control: no-store non sono idonee per la cache back-forward.

Per ottimizzare la cache back-forward, utilizza Cache-Control: no-store solo sulle pagine contenenti informazioni sensibili che non devono essere memorizzate nella cache.

Per le pagine in cui vuoi pubblicare sempre contenuti aggiornati, ma che non includono informazioni sensibili, utilizza Cache-Control: no-cache o Cache-Control: max-age=0. Questi comandi indicano al browser di riconvalidare i contenuti prima di pubblicarli e non influiscono sull'idoneità alla cache bfcache di una pagina perché il ripristino di una pagina da questa cache non richiede la memorizzazione nella cache HTTP.

Se i tuoi contenuti cambiano minuto per minuto, recupera gli aggiornamenti utilizzando l'evento pageshow per mantenere la pagina aggiornata come descritto nella sezione successiva.

Aggiorna dati inattivi o sensibili dopo il ripristino della cache back-forward

Se il tuo sito conserva i dati sullo stato degli utenti, in particolare se questi dati includono informazioni utente sensibili, devono essere aggiornati o cancellati dopo che una pagina viene ripristinata dalla cache back-forward.

Ad esempio, se un utente si disconnette da un sito su un computer pubblico e l'utente successivo fa clic sul pulsante Indietro, i dati inattivi di bfcache potrebbero includere dati privati che il primo utente prevedeva di essere cancellati al momento dell'uscita.

Per evitare situazioni come questa, aggiorna sempre la pagina dopo un evento pageshow se event.persisted è true:

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Do any checks and updates to the page
  }
});

Per alcune modifiche, ti consigliamo invece di forzare un ricaricamento completo, conservando la cronologia di navigazione per le navigazioni in avanti. Il seguente codice verifica la presenza di un cookie specifico del sito nell'evento pageshow e, se il cookie non viene trovato, viene ricaricato:

window.addEventListener('pageshow', (event) => {
  if (event.persisted && !document.cookie.match(/my-cookie)) {
    // Force a reload if the user has logged out.
    location.reload();
  }
});

Ripristino degli annunci e della cache back-forward

Si può avere la tentazione di evitare di utilizzare "bfcache" in modo che la pagina possa pubblicare un nuovo insieme di annunci a ogni navigazione a ritroso o in avanti. Tuttavia, ciò è negativo per il rendimento del sito e non aumenta costantemente il coinvolgimento con gli annunci. Ad esempio, un utente potrebbe voler tornare a una pagina per fare clic su un annuncio, ma se la pagina viene ricaricata anziché ripristinata dalla cache back-forward, potrebbe mostrare un annuncio diverso. Ti consigliamo di utilizzare i test A/B per determinare la strategia migliore per la tua pagina.

Per i siti che vogliono aggiornare gli annunci al ripristino della cache bfcache, puoi aggiornare solo gli annunci nell'evento pageshow quando event.persisted è true senza influire sulle prestazioni della pagina, come in questo esempio di tag di pubblicazione di Google. Per ulteriori informazioni sulle best practice per il tuo sito, rivolgiti al tuo fornitore di annunci.

Evita riferimenti a window.opener

Nei browser precedenti, se una pagina è stata aperta utilizzando window.open() da un link con target=_blank, senza specificare rel="noopener", la pagina di apertura avrà un riferimento all'oggetto finestra della pagina aperta.

Oltre a rappresentare un rischio per la sicurezza, una pagina con un riferimento window.opener non nullo non può essere inserita in sicurezza nella cache back-forward perché potrebbe danneggiare le pagine che tentano di accedervi.

Per evitare questi rischi, utilizza rel="noopener" per impedire la creazione di riferimenti window.opener. Questo è il comportamento predefinito in tutti i browser moderni. Se il tuo sito deve aprire una finestra e controllarla utilizzando window.postMessage() o facendo riferimento direttamente all'oggetto finestra, né la finestra aperta né l'elemento di apertura sono idonei per la cache back-forward.

Chiudi le connessioni aperte prima che l'utente esca dalla pagina

Come accennato in precedenza, quando una pagina viene inserita nella cache back-forward mette in pausa tutte le attività JavaScript pianificate e le riprende quando la pagina viene estratta dalla cache.

Se queste attività JavaScript pianificate accedono solo alle API DOM o ad altre API isolate nella pagina corrente, metterle in pausa quando la pagina non è visibile all'utente non causa problemi.

Tuttavia, se queste attività sono collegate ad API accessibili anche da altre pagine nella stessa origine (ad esempio: IndexedDB, Web Lock e WebSocket), la loro messa in pausa potrebbe interrompere le pagine impedendo l'esecuzione del codice su quelle pagine.

Di conseguenza, alcuni browser non tenteranno di inserire una pagina in bfcache se presenta uno dei seguenti elementi:

Se la tua pagina utilizza una di queste API, ti consigliamo vivamente di chiudere le connessioni e di rimuovere o scollegare gli osservatori durante l'evento pagehide o freeze. Ciò consente al browser di memorizzare nella cache la pagina in modo sicuro senza il rischio di inficiare altre schede aperte. Quindi, se la pagina viene ripristinata dalla cache back-forward, puoi riaprirla o riconnetterti a queste API durante l'evento pageshow o resume.

L'esempio seguente mostra come assicurarsi che le pagine che utilizzano IndexedDB siano idonee per la cache bfcache chiudendo una connessione aperta nell'elenco degli eventi pagehide:

let dbPromise;
function openDB() {
  if (!dbPromise) {
    dbPromise = new Promise((resolve, reject) => {
      const req = indexedDB.open('my-db', 1);
      req.onupgradeneeded = () => req.result.createObjectStore('keyval');
      req.onerror = () => reject(req.error);
      req.onsuccess = () => resolve(req.result);
    });
  }
  return dbPromise;
}

// Close the connection to the database when the user leaves.
window.addEventListener('pagehide', () => {
  if (dbPromise) {
    dbPromise.then(db => db.close());
    dbPromise = null;
  }
});

// Open the connection when the page is loaded or restored from bfcache.
window.addEventListener('pageshow', () => openDB());

Verifica che le pagine possano essere memorizzate nella cache

Chrome DevTools può aiutarti a testare le tue pagine per assicurarti che siano ottimizzate per la cache back-forward e a identificare eventuali problemi che potrebbero impedirne l'idoneità.

Per testare una pagina:

  1. Vai alla pagina in Chrome.
  2. In DevTools, vai ad Applicazione > Cache back-forward.
  3. Fai clic sul pulsante Esegui test. DevTools prova a uscire e a tornare indietro per determinare se la pagina può essere ripristinata dalla cache back-forward.
Riquadro della cache back-forward in DevTools
Il riquadro Cache back-forward in DevTools.

Se il test ha esito positivo, nel riquadro viene visualizzato il messaggio "Ripristinato dalla cache back-forward". In caso contrario, il riquadro ne indica il motivo. Per un elenco completo dei motivi, consulta l'articolo Elenco dei motivi per cui non è stato ripristinato per Chromium.

Se il motivo è qualcosa che puoi rivolgerti allo sviluppatore, il riquadro lo contrassegna come praticabile.

Errore durante il reporting di DevTools per il ripristino di una pagina dalla cache back-forward
Un test della cache back-forward non riuscito con un risultato utilizzabile.

In questa immagine, l'uso di un listener di eventi unload rende la pagina non idonea per la cache back-forward. Puoi risolvere il problema passando da unload all'utilizzo di pagehide:

Cosa fare
window.addEventListener('pagehide', ...);
Cosa non fare
window.addEventListener('unload', ...);

Lighthouse 10.0 ha anche aggiunto un controllo della cache back-forward, che esegue un test simile. Per ulteriori informazioni, consulta la documentazione relativa al controllo della cache bfcache.

In che modo la cache back-forward influisce sull'analisi e sulla misurazione delle prestazioni

Se utilizzi uno strumento di analisi per monitorare le visite al tuo sito, potresti notare una diminuzione del numero totale di visualizzazioni di pagina registrate in quanto Chrome abilita la cache back-forward per più utenti.

Infatti, probabilmente stai già sottostimando le visualizzazioni di pagina da altri browser che implementano bfcache perché la maggior parte delle librerie di analisi più diffuse non monitora i ripristini di bfcache come nuove visualizzazioni di pagina.

Per includere i ripristini di bfcache nel conteggio delle visualizzazioni di pagina, imposta i listener per l'evento pageshow e controlla la proprietà persisted.

L'esempio seguente mostra come eseguire questa operazione con Google Analytics. Altri strumenti di analisi utilizzano probabilmente una logica simile:

// Send a pageview when the page is first loaded.
gtag('event', 'page_view');

window.addEventListener('pageshow', (event) => {
  // Send another pageview if the page is restored from bfcache.
  if (event.persisted) {
    gtag('event', 'page_view');
  }
});

Misura il rapporto di successi della cache back-forward

Per identificare le pagine che non utilizzano ancora bfcache, misura il tipo di navigazione per i caricamenti delle pagine nel seguente modo:

// Send a navigation_type when the page is first loaded.
gtag('event', 'page_view', {
   'navigation_type': performance.getEntriesByType('navigation')[0].type;
});

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Send another pageview if the page is restored from bfcache.
    gtag('event', 'page_view', {
      'navigation_type': 'back_forward_cache';
    });
  }
});

Calcola il rapporto di hit della cache back-forward utilizzando i conteggi per le navigazioni back_forward e back_forward_cache.

I motivi per cui una navigazione a ritroso o in avanti potrebbe non utilizzare bfcache includono il seguente comportamento dell'utente:

  • Chiusura e riavvio del browser.
  • Duplicazione di una scheda.
  • Chiusura e ripristino di una scheda.

In alcuni di questi casi, il browser potrebbe mantenere il tipo di navigazione originale e mostrare un tipo di back_forward nonostante non si tratti di navigazioni indietro o in avanti. Anche quando i tipi di navigazione sono riportati correttamente, la cache back-forward viene eliminata periodicamente per risparmiare memoria.

Per questo motivo, i proprietari di siti web non possono aspettarsi un rapporto di hit dalla cache del bfcache del 100% per tutte le navigazioni back_forward. Tuttavia, misurare il rapporto può aiutarti a identificare le pagine che impediscono l'utilizzo della cache back-forward.

Il team di Chrome sta lavorando a un'API NotRestoredReasons che consenta di scoprire i motivi per cui le pagine non utilizzano la cache back-forward, in modo che gli sviluppatori possano migliorare le percentuali di successo della cache back-forward.

Misurazione del rendimento

Inoltre, la cache bfcache può influire negativamente sulle metriche sulle prestazioni raccolte sul campo, in particolare sulle metriche che misurano i tempi di caricamento delle pagine.

Poiché le navigazioni con bfcache ripristinano una pagina esistente, anziché avviare un nuovo caricamento pagina, il numero totale di caricamenti pagina raccolti diminuisce quando questa cache è abilitata. Tuttavia, le sostituzioni della cache del caricamento della pagina sono probabilmente tra i caricamenti più veloci delle pagine nel tuo set di dati, perché i caricamenti delle pagine si ripetono, incluse le navigazioni back-forward, e in genere sono più veloci del primo caricamento della pagina a causa della memorizzazione nella cache HTTP. Pertanto, l'attivazione di bfcache può causare un caricamento delle pagine più lento per le tue analisi, nonostante migliori le prestazioni del sito per l'utente.

Esistono diversi modi per risolvere questo problema. Uno è annotare tutte le metriche di caricamento pagina con il rispettivo tipo di navigazione: navigate, reload, back_forward o prerender. In questo modo puoi continuare a monitorare le prestazioni all'interno di questi tipi di navigazione, anche se la distribuzione complessiva presenta valori negativi. Consigliamo questo approccio per le metriche relative al caricamento delle pagine non incentrate sull'utente, come Time to First Byte (TTFB).

Per le metriche incentrate sugli utenti come i Segnali web essenziali, un'opzione migliore è segnalare un valore che rappresenti in modo più accurato l'esperienza utente.

Impatto sui Segnali web essenziali

I Segnali web essenziali misurano l'esperienza dell'utente su una pagina web in base a una serie di dimensioni (velocità di caricamento, interattività, stabilità visiva). È importante che le metriche di Segnali web essenziali riflettano il fatto che gli utenti sperimentano il ripristino della cache back-forward con una navigazione più veloce rispetto ai caricamenti di pagine predefinite.

Gli strumenti che raccolgono e generano report sulle metriche di Segnali web essenziali, come il Report sull'esperienza utente di Chrome, trattano i ripristini della cache back-forward come visite a pagine separate nel set di dati. Sebbene non siano disponibili API di prestazioni web dedicate per misurare queste metriche dopo il ripristino della cache back-forward, puoi utilizzare le API web esistenti per approssimarne i valori:

  • Per la metrica Largest Contentful Paint (LCP), utilizza il delta tra il timestamp dell'evento pageshow e il timestamp del frame dipinto successivo, perché tutti gli elementi nel frame verranno dipinti contemporaneamente. In caso di ripristino di una cache back-forward, LCP e FCP corrispondono.
  • In Interaction to Next Paint (INP), continua a utilizzare Performance Observationr esistente, ma reimposta l'attuale valore CLS su 0.
  • Per Cumulative Layout Shift (CLS), continua a utilizzare Performance Observationr esistente, ma reimposta l'attuale valore CLS su 0.

Per ulteriori dettagli su come la cache back-forward influisce su ogni metrica, consulta le singole pagine delle guide alle metriche di Core Web Vitals. Per un esempio specifico di come implementare le versioni bfcache di queste metriche, consulta la pagina relativa all'aggiunta di PR alla libreria JS web-vitals.

Altre risorse