Evitare richieste di rete non necessarie con la cache HTTP

Il recupero delle risorse sulla rete è lento e costoso:

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

Come si evitano richieste di rete non necessarie? 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 generale di una raccolta di API della piattaforma web. Queste API sono supportate in tutti i browser:

Cache-Control

Supporto dei browser

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Origine

ETag

Supporto dei browser

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Origine

Last-Modified

Supporto dei browser

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Origine

Funzionamento della cache HTTP

Tutte le richieste HTTP effettuate dal browser vengono prima instradate alla cache del browser per verificare se esiste una risposta memorizzata nella cache valida che possa essere utilizzata per soddisfare la richiesta. In caso di corrispondenza, la risposta viene letta dalla cache, eliminando così 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 della richiesta) che sulla configurazione del tuo server web (che determinerà le intestazioni della risposta).

Per una panoramica concettuale più approfondita, consulta l'articolo sulla memorizzazione nella cache del HTTP di MDN.

Intestazioni delle richieste: utilizza i valori predefiniti (solitamente).

Esistono una serie di intestazioni importanti che dovrebbero essere incluse nelle richieste in uscita della tua applicazione web, ma quasi sempre il browser si occupa di impostarle per tuo conto quando invia le richieste. Le intestazioni delle richieste che influiscono sul controllo dell'aggiornamento, come If-None-Match e If-Modified-Since, vengono visualizzate in base alla comprensione del browser degli attuali valori 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, senza ulteriori sforzi.

Intestazioni della risposta: configura il server web

La parte più importante dell'impostazione della memorizzazione nella cache HTTP sono le intestazioni che il server web aggiunge a ogni risposta in uscita. Le seguenti intestazioni incidono tutte sul comportamento effettivo 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 singola risposta.
  • ETag Quando il browser trova una risposta scaduta nella cache, può inviare un piccolo token (di solito un hash dei contenuti del file) al server per verificare se il file è stato modificato. Se il server restituisce lo stesso token, il file è lo stesso e non è necessario scaricarlo di nuovo.
  • Last-Modified Questa intestazione ha la stessa finalità di ETag, ma utilizza una strategia basata sul tempo per determinare se una risorsa è stata modificata, anziché la strategia basata sui contenuti di ETag.

Alcuni server web dispongono di un supporto integrato per l'impostazione di tali intestazioni per impostazione predefinita, mentre altri le lasciano completamente fuori, a meno che non le configuri esplicitamente. I dettagli specifici della modalità di configurazione delle intestazioni variano notevolmente a seconda del server web che utilizzi e ti consigliamo di consultare la documentazione del server per ottenere dettagli più precisi.

Per evitare di eseguire ricerche, ecco le istruzioni per la configurazione di alcuni dei server web più utilizzati:

L'esclusione dell'intestazione della risposta Cache-Control non disattiva la memorizzazione nella cache HTTP. Invece, i browser individueranno con certezza il tipo di comportamento di memorizzazione nella cache più adatto a un determinato tipo di contenuti. Con ogni probabilità vorresti avere un maggiore controllo rispetto a quello che offre, quindi configura le intestazioni delle risposte.

Quali valori di intestazione della risposta dovresti usare?

Ci sono due scenari importanti che devi affrontare quando configuri intestazioni di risposta del server web.

Memorizzazione nella cache di lunga durata per gli URL sottoposti a controllo delle versioni

Supponiamo che il tuo server indichi ai browser di memorizzare nella cache un file CSS per un anno (Cache-Control: max-age=31536000), ma il tuo designer abbia appena apportato un aggiornamento di emergenza che devi implementare immediatamente. Come si avvisano i browser per aggiornare lo stato "inattivo" copia cache del file? Non è possibile, almeno non senza modificare l'URL della risorsa.

Dopo che il browser memorizza la risposta nella cache, la versione memorizzata nella cache viene utilizzata fino a quando non è più aggiornata, come stabilito da max-age o expires, oppure finché non viene rimossa dalla cache per qualche altro motivo, ad esempio l'utente svuota la cache del browser. Di conseguenza, utenti diversi potrebbero utilizzare versioni diverse del file al momento della creazione della 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) utilizzano una versione precedente della risposta.

Come ottenere il meglio da entrambi i mondi: 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 il suo contenuto cambia. In genere, puoi farlo incorporando un'impronta del file o un numero di versione nel nome del file, ad esempio style.x234dff.css.

Quando rispondi a richieste di URL che contengono "fingerprint" o informazioni 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 comunica al browser che, quando deve caricare lo stesso URL in qualsiasi momento nel corso del prossimo anno (31.536.000 secondi; il valore massimo supportato), può utilizzare immediatamente il valore nella cache HTTP, senza dover effettuare una richiesta di rete al server web. Ottimo lavoro, hai subito acquisito l'affidabilità e la velocità che derivano dall'evitare la rete!

Strumenti di creazione come Webpack sono in grado di automatizzare il processo di assegnazione di fingerprint hash agli URL degli asset.

Riconvalida del server per gli URL senza controllo delle versioni

Purtroppo, non per tutti gli URL caricati viene eseguito il controllo delle versioni. Forse non sei in grado di includere un passaggio di build prima di eseguire il deployment dell'app web, quindi non puoi aggiungere hash agli URL degli asset. E ogni applicazione web ha bisogno di file HTML: questi file non includeranno (quasi!) mai informazioni sul controllo delle versioni, poiché nessuno si preoccuperà di utilizzare la tua app web se ha bisogno di ricordare che l'URL da visitare è https://example.com/index.34def12.html. Che cosa puoi fare per questi URL?

Questo è uno scenario in cui bisogna ammettere la sconfitta. La memorizzazione nella cache HTTP da sola non è abbastanza potente per evitare del tutto la rete. Non preoccuparti: presto scoprirai i operatori dei servizi, che ti forniranno il supporto di cui abbiamo bisogno per far tornare la battaglia a tuo favore. Tuttavia, ci sono alcuni passaggi che puoi seguire per assicurarti che le richieste di rete siano il più rapide ed efficienti possibile.

I seguenti valori di Cache-Control possono aiutarti a ottimizzare dove e come gli URL senza versione vengono memorizzati nella cache:

  • no-cache. Questo indica al browser di riconvalidare ogni volta il server prima di utilizzare una versione dell'URL memorizzata nella cache.
  • no-store. Questo indica al browser e ad altre cache intermedie (come le reti CDN) di non archiviare mai alcuna versione del file.
  • private. I browser possono memorizzare nella cache il file, ma non le cache intermedie.
  • public. La risposta può essere archiviata da qualsiasi cache.

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

Anche l'impostazione di ETag o Last-Modified può essere utile. Come menzionato nelle intestazioni della risposta, ETag e Last-Modified hanno entrambi lo stesso scopo: stabilire se il browser deve scaricare di nuovo un file memorizzato nella cache che è scaduto. Ti consigliamo di utilizzare la lingua ETag perché è più precisa.

Supponiamo che siano trascorsi 120 secondi dal recupero iniziale e che il browser abbia avviato una nuova richiesta per la stessa risorsa. Innanzitutto, il browser controlla la cache HTTP e trova la risposta precedente. Purtroppo il browser non può utilizzare la risposta precedente perché quella è 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 già presenti 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 un'altra fingerprint dei contenuti del file. Il browser non ha bisogno di sapere come viene generata l'impronta; deve solo inviarlo al server alla successiva richiesta. Se l'impronta è ancora la stessa, significa che la risorsa non è cambiata e il browser può saltare il download.

Se imposti ETag o Last-Modified, la richiesta di riconvalida diventa molto più efficiente perché attiva le intestazioni della richiesta If-Modified-Since o If-None-Match menzionate in Intestazioni della richiesta.

Quando un server web configurato correttamente vede le intestazioni delle richieste in entrata, può confermare se la versione della risorsa già presente nel browser nella cache HTTP corrisponde all'ultima versione sul server web. Se viene trovata una corrispondenza, il server può rispondere con una risposta HTTP 304 Not Modified, che equivale a "Continua a usare quello che hai già". I dati da trasferire quando si invia questo tipo di risposta sono pochi, quindi di solito è molto più veloce rispetto a dover inviare una copia della risorsa richiesta.

Visualizzazione di un client che richiede una risorsa e di un 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 l'intero file solo se il 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 quanto tempo il file deve essere memorizzato nella cache (Cache-Control: max-age=120).

Riepilogo

La cache HTTP è un modo efficace per migliorare le prestazioni di caricamento perché riduce le richieste di rete non necessarie. È supportata in tutti i browser e la sua 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 con controllo delle versioni.

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

Scopri di più

Se stai cercando di andare oltre le nozioni di base sull'utilizzo dell'intestazione Cache-Control, consulta la guida di Jake Archibald Caching best practice and max-age gochas di Jake Archibald.

Leggi l'articolo Amare la cache per indicazioni su come ottimizzare l'utilizzo della cache per i visitatori di ritorno.

Appendice: Altri suggerimenti

Se hai più tempo, ecco altri modi per ottimizzare l'utilizzo della cache HTTP:

  • Utilizza URL coerenti. Se pubblichi gli stessi contenuti su URL diversi, questi 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 la parte restante del file no (ad esempio il codice 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 viene aggiornato di frequente e una strategia di lunga durata nella memorizzazione nella cache per il codice che non cambia spesso.
  • Consulta la nuova direttiva stale-while-revalidate se un certo grado di obsolescenza è accettabile nelle norme relative al Cache-Control.

Appendice: diagramma di flusso Cache-Control

Diagramma di flusso
. Il processo decisionale per l'impostazione delle intestazioni Cache-Control.

Appendice: Cache-Control esempi

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 dalle cache intermedie) per un massimo di 10 minuti (60 secondi x 10 minuti).
public, max-age=31536000 La risposta può essere memorizzata da qualsiasi cache per un anno.
no-store La risposta non può essere memorizzata nella cache e deve essere recuperata completamente per ogni richiesta.