Performance
Cache-Control spiegato: header, max-age e immutable
Cache-Control decide quanto a lungo browser e CDN riusano una risposta. HTML in no-cache, asset con hash in immutable: la configurazione che regge.
In sintesi
- HTML va servito con no-cache, asset con hash nel nome con max-age lungo e immutable.
- Il 1 novembre 2026 l'asset hashed rispondeva immutable con cf-cache-status HIT, l'HTML no-cache con DYNAMIC.
- immutable è sicuro solo se il nome del file cambia a ogni build.
Cache-Control dice al browser e al CDN per quanto tempo possono riusare una risposta senza tornare al server. La configurazione che regge è una: HTML con no-cache, asset con hash nel nome e max-age=31536000, immutable.
Le direttive che contano
| Direttiva | Cosa fa | Dove usarla |
|---|---|---|
no-cache | salva la risposta e la rivalida a ogni uso | HTML e API dinamiche |
no-store | non salva nulla | risposte con dati personali |
max-age=31536000 | 365 giorni di freschezza | asset con hash nel nome |
immutable | nessuna rivalidazione, nemmeno su reload | asset con hash nel nome |
stale-while-revalidate | serve la copia vecchia mentre aggiorna | immagini e JSON non critici |
Il comando
curl -sS -o /dev/null -D - -H "Accept-Encoding: br, gzip" https://ui.eneawork.it/assets/index-B90Ihhpp.js
L'output mostra la risposta dell'edge, non quella del server: -D - stampa gli header e -o /dev/null scarta il corpo.
Perché il nome del file decide tutto
Un file con hash nel nome cambia URL a ogni build, quindi può essere immutabile. Un file con nome stabile, come /css/app.css, deve scadere o essere invalidato dal CDN a ogni deploy.
if (/\.(woff2?|ttf)$/i.test(filePath)) {
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
} else if (/\.(css|js|mjs)$/i.test(filePath)) {
res.setHeader("Cache-Control", "public, max-age=2592000, stale-while-revalidate=604800");
} else {
res.setHeader("Cache-Control", "no-cache");
}
È il codice che serve questo sito: font immutabili per un anno, CSS e JS senza hash per 30 giorni con rivalidazione in background, tutto il resto in no-cache. Il deploy deve invalidare il CDN per i file senza hash.
no-cache non significa «non salvare»
Con no-cache il browser conserva la copia e chiede conferma al server. Con no-store non conserva nulla. Con max-age=0 la copia è subito vecchia e viene rivalidata: la differenza con no-cache è più formale che pratica.
La limitazione
La misura descrive l'edge, non l'origine. Cloudflare ha risposto public, max-age=0 a una HEAD senza Accept-Encoding e no-cache alla GET con compressione: stessa URL, due header. Un HIT su un punto di presenza non dice nulla sugli altri.
Dove continuare
Il catalogo EneUI indicizza 111.985 componenti e 1.556 blocchi con preview. Il record spotlight card è una card con immagine: se la cambi, l'URL resta e la cache va invalidata. Per la compressione leggi Brotli contro gzip, per i livelli di cache di Next.js i quattro livelli, per il peso del bundle un import che aumenta il bundle.
Domande frequenti
Che differenza c'è tra no-cache e no-store?
no-cache permette di salvare la risposta ma impone la rivalidazione prima di riusarla. no-store vieta di salvarla, quindi è la scelta per i dati personali.
Posso usare immutable su tutto?
No. immutable è sicuro solo se l'URL cambia quando cambia il contenuto, quindi serve un hash nel nome del file. Su un nome stabile blocca gli aggiornamenti per un anno.
Serve ancora ETag con immutable?
Su un asset immutabile l'ETag resta innocuo ma inutile, perché nessun client lo userà. Sull'HTML l'ETag riduce i byte della rivalidazione, quindi conviene tenerlo.
Meglio max-age o s-maxage?
s-maxage vale per le cache condivise, come un CDN, e ha la precedenza su max-age. Usa s-maxage quando il CDN deve conservare più a lungo del browser.
Perché il browser rivalida anche con max-age lungo?
Succede con un reload forzato o quando la cache locale è piena e il browser elimina le voci. Con immutable il reload non produce richiesta, ma solo se la risposta precedente è ancora in cache.