Evitare richieste di rete non necessarie con la cache HTTP

Il recupero delle risorse sulla rete è lento e costoso:

  • Le risposte di grandi dimensioni richiedono molti roundtrip tra il browser e il server.
  • La pagina non verrà caricata finché tutte le risorse critiche non saranno state scaricate completamente.
  • Se una persona accede al tuo sito con un piano dati mobili limitato, ogni richiesta di rete non necessaria è uno spreco di denaro.

Come si possono evitare richieste di rete inutili? La cache HTTP del browser è la prima linea di difesa. Non è necessariamente l'approccio più potente o flessibile e hai un controllo limitato sulla durata delle risposte memorizzate nella cache, ma è efficace, è supportato in tutti i browser e non richiede molto lavoro.

Questa guida illustra le nozioni di base per un'implementazione efficace della memorizzazione nella cache HTTP.

Compatibilità del browser

In realtà non esiste una sola API chiamata Cache HTTP. È il nome generico di una raccolta di API delle piattaforme web. Queste API sono supportate in tutti i browser:

Funzionamento della cache HTTP

Tutte le richieste HTTP effettuate dal browser vengono innanzitutto instradate alla cache del browser per verificare se è disponibile una risposta memorizzata nella cache che può essere utilizzata per soddisfare la richiesta. Se c'è una corrispondenza, la risposta viene letta dalla cache, il che elimina sia la latenza di rete sia i costi dei dati sostenuti dal trasferimento.

Il comportamento della cache HTTP è controllato da una combinazione di intestazioni della richiesta e intestazioni della risposta. In uno scenario ideale, avrai il controllo sia sul codice della tua applicazione web (che determinerà le intestazioni delle richieste) sia sulla configurazione del tuo server web (che determinerà le intestazioni delle risposte).

Per una panoramica concettuale più approfondita, consulta l'articolo di MDN HTTP Caching (Memorizzazione nella cache HTTP).

Intestazioni delle richieste: mantieni i valori predefiniti (di solito)

Sebbene esista una serie di intestazioni importanti che devono essere incluse nelle richieste in uscita dell'applicazione web, quasi sempre il browser si occupa di impostarle per tuo conto quando effettua le richieste. Le intestazioni delle richieste che influiscono sulla verifica dell'aggiornamento, come If-None-Match e If-Modified-Since, vengono visualizzate in base alla comprensione da parte del browser dei valori correnti nella cache HTTP.

Questa è una buona notizia: significa che puoi continuare a includere tag come <img src="my-image.png"> nel tuo codice HTML e il browser si occupa automaticamente della memorizzazione nella cache HTTP per te, senza alcuno sforzo in più.

Intestazioni della risposta: configura il server web

La parte più importante della configurazione della memorizzazione nella cache HTTP è costituita dalle intestazioni aggiunte dal server web a ogni risposta in uscita. Tutte le intestazioni riportate di seguito contribuiscono a un efficace comportamento della memorizzazione nella cache:

  • Cache-Control. Il server può restituire un'istruzione Cache-Control per specificare come e per quanto tempo il browser e altre cache intermedie devono memorizzare nella cache la risposta singola.
  • ETag. Quando il browser trova una risposta memorizzata nella cache, può inviare un piccolo token (di solito un hash dei contenuti del file) al server per verificare se il file è cambiato. Se il server restituisce lo stesso token, il file è lo stesso e non è necessario scaricarlo di nuovo.
  • Last-Modified. Questa intestazione ha lo stesso scopo di ETag, ma utilizza una strategia basata sul tempo per determinare se una risorsa è cambiata, al contrario della strategia basata sui contenuti di ETag.

Alcuni server web dispongono di supporto integrato per l'impostazione di queste intestazioni per impostazione predefinita, mentre altri le lasciano completamente rimosse, a meno che non le configuri esplicitamente. I dettagli specifici su come configurare le intestazioni variano notevolmente a seconda del server web utilizzato. Per informazioni più dettagliate, consulta la documentazione del server.

Per evitare di dover eseguire ricerche, ecco le istruzioni per configurare alcuni server web più diffusi:

Se viene esclusa l'intestazione della risposta Cache-Control, la memorizzazione nella cache HTTP non viene disattivata. Al contrario, i browser prevedono in modo efficace quale tipo di comportamento di memorizzazione nella cache è più adatto per un determinato tipo di contenuti. Probabilmente vuoi avere un controllo maggiore di quello offerto, pertanto dedica tutto il tempo necessario alla configurazione delle intestazioni delle risposte.

Quali valori dell'intestazione della risposta devi utilizzare?

Sono due gli scenari importanti da considerare durante la configurazione delle intestazioni di risposta del server web.

Memorizzazione nella cache di lunga durata per gli URL con versione

In che modo gli URL con versioni possono contribuire alla strategia di memorizzazione nella cache
Gli URL con più versioni sono una buona pratica perché semplificano l'annullamento della validità delle risposte memorizzate nella cache.

Supponiamo che il tuo server indichi ai browser di memorizzare nella cache un file CSS per 1 anno (Cache-Control: max-age=31536000), ma che il tuo designer abbia appena apportato un aggiornamento di emergenza che devi implementare immediatamente. Come comunichi ai browser di aggiornare la copia cache "inattiva" del file? Non puoi, almeno non senza modificare l'URL della risorsa. Dopo che il browser ha memorizzato la risposta nella cache, la versione memorizzata nella cache viene utilizzata fino a quando non è più aggiornata, come stabilito da max-age o expires, o fino a quando non viene eliminata dalla cache per altri motivi, ad esempio quando l'utente svuota la cache del browser. Di conseguenza, utenti diversi potrebbero finire per utilizzare versioni diverse del file quando viene creata la pagina: gli utenti che hanno appena recuperato la risorsa utilizzano la nuova versione, mentre gli utenti che hanno memorizzato nella cache una copia precedente (ma ancora valida) usano una versione precedente della risposta. Come ottenere il meglio da entrambe le soluzioni: memorizzazione nella cache lato client e aggiornamenti rapidi? Puoi modificare l'URL della risorsa e forzare l'utente a scaricare la nuova risposta ogni volta che cambiano i suoi contenuti. In genere, puoi farlo incorporando nel nome file un'impronta o un numero di versione del file, ad esempio style.x234dff.css.

Quando rispondi alle richieste di URL che contengono informazioni sul "fingerprint" o sul controllo delle versioni e i cui contenuti non devono mai essere modificati, aggiungi Cache-Control: max-age=31536000 alle tue risposte.

L'impostazione di questo valore indica al browser che,quando deve caricare lo stesso URL in qualsiasi momento nell'anno successivo (31.536.000 secondi,il valore massimo supportato), può utilizzare immediatamente il valore nella cache HTTP, senza dover effettuare alcuna richiesta di rete al server web. Ottimo lavoro: hai subito acquisito l'affidabilità e la velocità che derivano dall'aver eliminato la rete.

Strumenti come il webpack possono automatizzare il processo di assegnazione delle fingerprint di hash agli URL degli asset.

Riconvalida del server per gli URL senza versione

Purtroppo, non tutti gli URL caricati hanno il controllo delle versioni. Ad esempio, potresti non essere in grado di includere un passaggio di creazione prima di eseguire il deployment della tua app web, quindi non puoi aggiungere hash agli URL degli asset. Ogni applicazione web ha bisogno di file HTML, questi non includeranno (quasi) le informazioni sul controllo delle versioni, in quanto nessuno si occuperà di utilizzare la tua app web se deve ricordare che l'URL da visitare è https://example.com/index.34def12.html. Cosa puoi fare per questi URL?

Questo è uno dei casi in cui è necessario ammettere la sconfitta. La memorizzazione nella cache HTTP da sola non è sufficiente per evitare del tutto la rete. (Non preoccuparti, presto scoprirai i lavoratori dei servizi, che ti forniranno il supporto di cui abbiamo bisogno per restituire la battaglia a tuo favore). Tuttavia, puoi seguire alcuni passaggi per assicurarti che le richieste di rete siano il più rapide ed efficienti possibile.

I seguenti valori Cache-Control possono aiutarti a ottimizzare dove e come vengono memorizzati nella cache gli URL di cui è stata annullata la versione:

  • no-cache. In questo modo indichi al browser che deve riconvalidarlo ogni volta prima di utilizzare una versione dell'URL memorizzata nella cache.
  • no-store. In questo modo il browser e altre cache intermedie (come le CDN) non archiviano mai nessuna versione del file.
  • private. I browser possono memorizzare il file nella cache, ma non quelle intermedie.
  • public. La risposta può essere memorizzata in qualsiasi cache.

Consulta l'Appendice: diagramma di flusso Cache-Control per visualizzare il processo per decidere quali valori di Cache-Control utilizzare. Tieni inoltre presente che Cache-Control può accettare un elenco di istruzioni separate da virgole. Consulta l'Appendice: esempi di Cache-Control.

Oltre a questo, può essere utile anche impostare una delle due intestazioni di risposta aggiuntive: ETag o Last-Modified. Come menzionato nelle intestazioni della risposta, ETag e Last-Modified hanno entrambi lo stesso scopo: determinare se il browser deve scaricare di nuovo un file memorizzato nella cache che è scaduto. ETag è l'approccio consigliato perché è più preciso.

Esempio di tag ETag

Supponiamo che siano trascorsi 120 secondi dal recupero iniziale e che il browser abbia avviato una nuova richiesta per la stessa risorsa. In primo luogo, il browser controlla la cache HTTP e trova la risposta precedente. Sfortunatamente, il browser non può utilizzare la risposta precedente perché la risposta è ora scaduta. A questo punto, il browser potrebbe inviare una nuova richiesta e recuperare la nuova risposta completa. Tuttavia, non è efficiente perché se la risorsa non è cambiata, non c'è motivo di scaricare le stesse informazioni che sono già nella cache. Questo è il problema per cui i token di convalida, come specificato nell'intestazione ETag, sono progettati per risolvere. Il server genera e restituisce un token arbitrario, che in genere è un hash o qualche altra fingerprint dei contenuti del file. Il browser non ha bisogno di sapere come viene generata l'impronta, deve solo inviarla al server alla richiesta successiva. Se l'impronta è sempre la stessa, la risorsa non è cambiata e il browser può saltare il download.

Se imposti ETag o Last-Modified, renderai la richiesta di riconvalida molto più efficiente. Finiscono per attivare le intestazioni della richiesta If-Modified-Since o If-None-Match menzionate in Intestazioni delle richieste.

Quando un server web configurato correttamente rileva le intestazioni delle richieste in entrata, può confermare se la versione della risorsa che il browser ha già nella sua cache HTTP corrisponde all'ultima versione sul server web. Se viene rilevata una corrispondenza, il server può rispondere con una risposta HTTP 304 Not Modified, che equivale a dire "Continua a usare i contenuti che hai già". I dati da trasferire quando si invia questo tipo di risposta sono davvero scarsi, quindi di solito è molto più rapido che dover inviare una copia della risorsa effettiva richiesta.

Diagramma di un client che richiede una risorsa e del server che risponde con un&#39;intestazione 304.
Il browser richiede /file al server e include l'intestazione If-None-Match per indicare al server di restituire il file completo solo se il valore ETag del file sul server non corrisponde al valore If-None-Match del browser. In questo caso, i 2 valori corrispondono, quindi il server restituisce una risposta 304 Not Modified con istruzioni per il periodo di tempo a lungo termine del file nella cache (Cache-Control: max-age=120).

Riepilogo

La cache HTTP è un modo efficace per migliorare le prestazioni del caricamento perché riduce le richieste di rete non necessarie. È supportato in tutti i browser e la configurazione non richiede troppo lavoro.

Le seguenti configurazioni di Cache-Control sono un buon inizio:

  • Cache-Control: no-cache per le risorse che devono essere riconvalidate con il server prima di ogni utilizzo.
  • Cache-Control: no-store per le risorse che non devono mai essere memorizzate nella cache.
  • Cache-Control: max-age=31536000 per le risorse sottoposte al controllo delle versioni.

Inoltre, l'intestazione ETag o Last-Modified può aiutarti a riconvalidare in modo più efficiente le risorse della cache scadute.

Scopri di più

Se stai cercando di andare oltre le nozioni di base sull'utilizzo dell'intestazione Cache-Control, consulta la guida Best practice per la memorizzazione nella cache e max-age gotchas di Jake Archibald.

Consulta Ama la cache per istruzioni su come ottimizzare l'utilizzo della cache per i visitatori di ritorno.

Appendice: Altri suggerimenti

Se hai più tempo a disposizione, di seguito sono riportati ulteriori metodi per ottimizzare l'utilizzo della cache HTTP:

  • Utilizza URL coerenti. Se pubblichi gli stessi contenuti su URL diversi, quei contenuti verranno recuperati e archiviati più volte.
  • Riduci al minimo il tasso di abbandono. Se parte di una risorsa (ad esempio un file CSS) viene aggiornata di frequente, mentre il resto del file no (ad esempio, il codice della libreria), valuta la possibilità di suddividere il codice che viene aggiornato di frequente in un file separato e di utilizzare una strategia di memorizzazione nella cache di breve durata per il codice che si aggiorna di frequente e una strategia di durata della memorizzazione nella cache lunga per il codice che non cambia spesso.
  • Controlla la nuova istruzione stale-while-revalidate se un certo grado di inattività è accettabile nel tuo criterio Cache-Control.

Appendice: diagramma di flusso Cache-Control

Diagramma di flusso

Appendice: esempi Cache-Control

Valore Cache-Control Spiegazione
max-age=86400 La risposta può essere memorizzata nella cache dai browser e dalle cache intermedie per un massimo di 1 giorno (60 secondi x 60 minuti x 24 ore).
private, max-age=600 La risposta può essere memorizzata nella cache dal browser (ma non da quelle intermedie) per un massimo di 10 minuti (60 secondi x 10 minuti).
public, max-age=31536000 La risposta può essere archiviata in qualsiasi cache per 1 anno.
no-store La risposta non può essere memorizzata nella cache e deve essere recuperata per intero a ogni richiesta.