Cosa fa davvero una CDN a una richiesta
Un utente a Tokyo che carica una pagina ospitata su un server in Virginia paga quella distanza due volte: una per far arrivare la richiesta, una per far tornare la risposta. La fibra ottica viaggia a una frazione fissa della velocità della luce, e quel solo tragitto costa già 150-200 ms di andata e ritorno prima che l’origine abbia fatto qualsiasi lavoro. Aggiungi una query al database e un salto di rete lento, e una pagina che sembra istantanea per chi sta vicino al server diventa lenta dall’altra parte del pianeta.
Una CDN, o content delivery network, risolve questo problema per i contenuti che si possono copiare: immagini, CSS, bundle JavaScript, segmenti video, a volte intere pagine HTML. Una rete di server posizionati vicino agli utenti mantiene copie di quei contenuti e risponde direttamente, così la maggior parte delle richieste non attraversa mai l’oceano. L’origine resta la versione autorevole. La CDN si mette davanti e intercetta quante più richieste può servire da una copia.
Come una richiesta sceglie quale server risponde
Chi gestisce una CDN fa girare server in molte sedi fisiche, di solito chiamate Points of Presence, o PoP: data center dentro o vicino alle grandi città, ognuno con una porzione dei contenuti in cache e un percorso veloce verso l’origine per tutto il resto. Due meccanismi decidono quale PoP gestisce una data richiesta, e le CDN più grandi li usano entrambi.
Il routing basato su DNS risolve lo stesso hostname a indirizzi IP diversi in base a dove arriva la query. Una query DNS per assets.example.com da un resolver a Francoforte riceve l’IP di un PoP di Francoforte; la stessa query da San Paolo riceve un IP completamente diverso.
L’anycast va oltre. Molti PoP annunciano lo stesso indirizzo IP via BGP, e il normale routing di internet decide quale di questi un dato pacchetto raggiunge davvero, in base alla topologia di rete piuttosto che alla geografia. La CDN non fa mai questa scelta. Anche il failover è più veloce: quando un PoP esce dalla rete, i router smettono di inviargli traffico, senza nessuna modifica DNS da propagare.
| |
In entrambi i casi, il client non parla mai direttamente con l’origine per i contenuti in cache. Parla con qualunque PoP il livello di routing abbia scelto, e quel PoP ha già il contenuto oppure lo va a prendere una volta sola e se lo ricorda. Ogni PoP fa il lavoro di un reverse proxy davanti all’origine: termina la connessione del client e decide cosa succede dopo, con una cache attaccata e migliaia di copie in esecuzione contemporaneamente in tutto il mondo.
Come capire se hai avuto un cache hit o un cache miss
La prima richiesta per un URL attraverso un dato PoP è un cache miss. Il PoP non ha nulla salvato, quindi va a prendere il contenuto dall’origine, ne salva una copia secondo le regole indicate nella risposta, e la restituisce. Ogni richiesta successiva per quell’URL, da qualsiasi client instradato allo stesso PoP, è un hit: servita dallo storage del PoP, senza coinvolgere l’origine, finché la copia non scade o viene invalidata.
Un comando ti dice quale dei due hai ottenuto:
| |
Una risposta in cache porta un header proprietario che indica l’esito. Cloudflare aggiunge CF-Cache-Status con valori come HIT, MISS, EXPIRED (il TTL è scaduto, quindi il PoP l’ha ripreso dall’origine), REVALIDATED (l’origine ha confermato che la vecchia copia era ancora valida), oppure DYNAMIC (la CDN ha deciso che questa risposta non è affatto cacheable). L’equivalente di Fastly è X-Cache: HIT oppure X-Cache: MISS. Esegui due volte la stessa richiesta su un URL cacheable e la seconda dovrebbe passare da MISS a HIT.
| |
age è da quanti secondi il PoP ha recuperato questa copia dall’origine. Qui sono 412 secondi su un TTL di 3600, quindi alla copia resta quasi un’ora prima che la prossima richiesta scateni un nuovo fetch.
Come Cache-Control dice alla CDN cosa mettere in cache
La CDN non indovina cosa mettere in cache. Glielo dice l’origine, tramite l’header Cache-Control su ogni risposta.
| Direttiva | Cosa fa |
|---|---|
max-age=N | Per quanti secondi qualsiasi cache — browser o CDN — può conservare la risposta |
s-maxage=N | Per quanto tempo una cache condivisa (la CDN) può conservarla — sovrascrive max-age specificamente per la CDN, i browser continuano a seguire max-age |
public | Permette esplicitamente la cache anche su risposte che altrimenti sembrerebbero private (per esempio quelle con un header Authorization) |
private | Solo il browser può mettere in cache questa risposta; la CDN non deve farlo |
no-cache | Le cache possono salvarla ma devono rivalidarla con l’origine prima di servirla di nuovo |
no-store | Non va mai messa in cache da nessuna parte, né browser né CDN |
Per un asset statico con un nome file con hash (app.a3f9c1.css), l’impostazione tipica è aggressiva: Cache-Control: public, max-age=31536000, immutable. Il nome del file cambia ogni volta che cambia il contenuto, quindi un anno è sicuro. Una copia scaduta di app.a3f9c1.css è una contraddizione, perché quel nome esatto punta sempre e solo a una versione del file.
Per una pagina HTML o una risposta API che vuoi in cache ma più fresca, Cache-Control: public, s-maxage=60, max-age=0 la tiene sulla CDN per un minuto mentre i browser rivalidano ogni volta. È lo stesso principio di cache a TTL breve che sta dietro il funzionamento del caching con Redis, applicato tramite un header HTTP invece che nel codice dell’applicazione.
Sbagliarlo nella direzione opposta fa trapelare dati. Servire una pagina personale per utente con Cache-Control: public, max-age=3600 e senza Vary sul cookie di sessione significa che la CDN può consegnare la pagina in cache dell’utente A all’utente B.
Come invalidare una cache CDN scaduta
A volte un TTL non basta: hai rilasciato una correzione e non puoi aspettare un’ora perché raggiunga tutti. Ogni CDN espone un’API di purge o un’azione da dashboard per questo. Le dici che un URL, o un’intera zona, non è più valido, e la richiesta successiva viene trattata come un cache miss indipendentemente da quanto TTL restava.
L’abitudine più economica è non doverlo mai fare. Versiona il nome del file con un hash del contenuto o un parametro di query incrementale, così ogni deploy è un URL nuovo con una propria voce di cache, e la vecchia copia scaduta smette del tutto di essere richiesta. Riserva il purge per ciò che non puoi aggirare: un incidente, una richiesta di rimozione legale, una cache avvelenata da un header Vary configurato male.
Quando una CDN non serve, e cosa ti costa
Le risposte personalizzate non sono cacheable in nessun senso utile. Una dashboard per utente autenticato, un carrello, qualsiasi cosa legata a una sessione: la richiesta successiva vorrà per forza un contenuto diverso. Mettere una CDN davanti a quegli endpoint non porta nulla, tranne la falsa sensazione di aver aggiunto una cache, e se le regole di cache sono sbagliate rischia attivamente di servire la risposta privata di un utente a un altro.
Una CDN aggiunge anche un livello da debuggare. “Funziona in locale ma non in produzione” a volte significa che il codice è a posto e un PoP sta servendo una risposta messa in cache prima della tua correzione; la richiesta non raggiunge l’origine finché il TTL non scade o non fai il purge.
Anche la forma del traffico conta. Uno strumento interno, o un servizio regionale senza pubblico internazionale, paga per PoP che non usa mai e recupera una latenza che non aveva mai avuto. Una CDN risolve due problemi specifici: il tragitto verso utenti lontani, e il carico ripetuto sull’origine. Se nessuno dei due è il tuo problema, è infrastruttura senza niente da fare.
Come verificare se hai già una CDN
Esegui curl -I su un asset statico di un sito che gestisci. Se la risposta porta cache-control, un header age, o uno stato proprietario come CF-Cache-Status o X-Cache, qualcosa sta già facendo da cache davanti alla tua origine, che tu l’abbia configurato di proposito o che il tuo hosting lo includa di default. Se quegli header mancano su asset richiesti da più di una regione, è questo il caso concreto per aggiungerne una: Cloudflare, AWS CloudFront e Fastly permettono tutte di mettere un dominio dietro a un piano gratuito o a consumo in meno di un’ora, senza dover cambiare il codice dell’origine.