Cache-Control — cosa è, direttive e gestione della cache

Autore: IT Sectr Pubblicato: 2026-03-09 Tempo di lettura: 9 min

Cache-Control è un'intestazione HTTP che definisce le regole di caching delle risorse lato client, server proxy e CDN utilizzando un insieme di direttive. A differenza dell'obsoleta intestazione Expires, Cache-Control supporta decine di combinazioni: max-age imposta la durata in secondi, private e public controllano la disponibilità della cache, no-cache e no-store — verifica forzata. Secondo Google Web Dev (2025), una corretta configurazione di Cache-Control può ridurre i tempi di caricamento delle pagine del 50-80% per le visite ripetute. Ciò rende l'intestazione criticamente importante per le prestazioni delle applicazioni web e mobili.

Punti chiave

  • Cache-Control — un'intestazione HTTP con direttive che controllano il caching su client, proxy e CDN
  • max-age — una direttiva chiave che imposta la durata della risorsa in secondi senza nuova convalida
  • private vs public — private consente la cache solo sul client, public anche su proxy e CDN
  • no-cache vs no-store — no-cache richiede la convalida prima dell'uso, no-store vieta completamente la cache
  • s-maxage — sovrascrive max-age per le cache condivise senza influenzare i browser

Cos'è Cache-Control?

Cache-Control è un'intestazione HTTP, standardizzata in HTTP/1.1 (RFC 7234), che consente al server di specificare come e per quanto tempo client, proxy e CDN possono memorizzare nella cache la risposta. A differenza di Expires (HTTP/1.0), Cache-Control utilizza direttive — comandi testuali combinati con virgole: Cache-Control: public, max-age=3600, must-revalidate. L'intestazione fornisce un controllo granulare su ogni anello della catena di caching.

Il caching è uno dei meccanismi fondamentali delle prestazioni delle applicazioni web e mobili. Senza di esso, ogni richiesta utente andrebbe direttamente al server, causando carico eccessivo e latenza. Cache-Control definisce tre livelli di cache: browser/applicazione (cache privata), server proxy (cache condivisa) e CDN (cache distribuita). Ogni livello interpreta le direttive in modo diverso.

Una configurazione errata di Cache-Control è una delle cause più comuni di problemi di prestazioni. Un caching troppo aggressivo fa sì che gli utenti vedano dati obsoleti. Un caching troppo debole porta a richieste eccessive al server e caricamento lento. Secondo Akamai (2025), ottimizzare Cache-Control per i contenuti statici riduce il carico del server del 70-90% e migliora il tempo di caricamento del 40-60% per gli utenti mobili.

Storia dell'intestazione

Cache-Control è apparso in HTTP/1.1 (RFC 2616, 1999) come sostituto di Expires. Expires aveva un problema fondamentale: utilizzava una data assoluta che dipendeva dai fusi orari del server e del client. Cache-Control ha risolto questo problema passando al tempo relativo (max-age in secondi dal momento della ricezione della risposta). Successivamente, in RFC 7234 (2014), sono state aggiunte nuove direttive: immutable per le risorse statiche, stale-while-revalidate e stale-if-error per la convalida differita.

Direttive di Cache-Control

Cache-Control include più di 10 direttive suddivise in tre gruppi: direttive di richiesta (client → server), direttive di risposta (server → client) ed estensioni. In pratica, lo sviluppo mobile utilizza 6-7 direttive di risposta principali che coprono il 95% degli scenari di caching. Esaminiamo ciascuna con esempi e raccomandazioni.

DirettivaSignificatoEsempio
max-ageDurata in secondi dal momento della rispostamax-age=3600 — 1 ora
s-maxagemax-age per cache condivisa (proxy, CDN)s-maxage=86400 — 1 giorno per CDN
publicConsente il caching a tutti (inclusi i proxy)public, max-age=3600
privateConsente la cache solo per il browser/applicazioneprivate, max-age=600
no-cacheNon usare senza convalida (304 richiesto)no-cache
no-storeVietare completamente il cachingno-store
must-revalidateDopo max-age, deve ricontrollare con l'originemax-age=3600, must-revalidate
immutableLa risorsa non cambierà (per risorse statiche versionate)max-age=31536000, immutable

max-age è la direttiva più importante. Vieta al client di effettuare una richiesta al server per il tempo specificato. Per le risorse statiche (CSS, JS, immagini), max-age viene solitamente impostato da 1 giorno a 1 anno. Per le risposte API — da 0 secondi (dati sempre freschi) a 5-10 minuti (dati di riferimento). s-maxage consente di impostare durate diverse per CDN e browser: il CDN conserva una copia per 1 giorno, il browser per 1 ora.

no-cache vs no-store

Queste due direttive vengono spesso confuse. no-cache non vieta il caching — richiede di convalidare la copia cache a ogni utilizzo tramite una richiesta condizionale (If-Modified-Since o If-None-Match). Se il server risponde con 304 — il client usa la cache. Se 200 — la aggiorna. no-store, invece, vieta completamente di salvare la risposta in qualsiasi cache, incluso il disco e la memoria. Usa no-store solo per i dati sensibili — token, dati di pagamento, documenti personali.

Cache-Control vs Expires

L'intestazione Expires (HTTP/1.0) specifica anch'essa la durata della risorsa ma utilizza una data assoluta: Expires: Thu, 03 Jul 2026 12:00:00 GMT. Cache-Control max-age utilizza il tempo relativo dal momento della risposta. La differenza è critica per i sistemi distribuiti: se server e client si trovano in fusi orari diversi, Expires può essere interpretato erroneamente. Cache-Control non ha questo problema — 3600 secondi sono sempre 3600 secondi.

Quando entrambe le intestazioni sono presenti, Cache-Control ha priorità su Expires. Ciò è definito in RFC 7234: “Se una risposta include un campo Cache-Control con la direttiva max-age, il destinatario DEVE ignorare il campo Expires.” In pratica, si raccomanda di non restituire Expires per i client moderni, poiché Cache-Control copre tutti gli scenari di Expires. Tuttavia, per la compatibilità all'indietro con proxy e browser vecchi, entrambe le intestazioni possono essere restituite.

Expires è sopravvissuto principalmente per i contenuti statici su Nginx e Apache — questi server aggiungono automaticamente entrambe le intestazioni. Se il tuo progetto incontra Expires senza Cache-Control, sostituiscilo con Cache-Control con max-age: la precisione del controllo della cache migliora e la dipendenza dal fuso orario viene eliminata. Per la migrazione, è sufficiente configurare il server per aggiungere Cache-Control invece di Expires.

nginx
# Nginx: Cache-Control per file statici
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 30d;
    add_header Cache-Control "public, immutable, max-age=2592000";
}

# Politiche diverse per diversi tipi di contenuto
location /api/config {
    expires -1;
    add_header Cache-Control "no-cache, must-revalidate";
}

location /api/static-data {
    expires 5m;
    add_header Cache-Control "public, max-age=300";
}

Nella configurazione Nginx, i file statici (CSS, JS, immagini) vengono impostati con Cache-Control per 30 giorni con l'attributo immutable — questo attributo comunica al browser che la risorsa non cambia mai a questo URL (versionamento tramite hash nel nome del file). Gli endpoint API utilizzano no-cache per i dati dinamici e public con un max-age breve per i dati di riferimento — elenchi frequentemente richiesti e raramente modificati.

Caching nelle applicazioni mobili

Nelle applicazioni mobili, Cache-Control gioca un ruolo speciale a causa dei limiti delle reti mobili: alta latenza, connessione instabile, limiti di traffico. Un caching appropriato consente di visualizzare i dati all'utente istantaneamente, anche offline, e di aggiornarli in background. OkHttp su Android e URLSession su iOS dispongono di sistemi di cache integrati che rispettano Cache-Control.

OkHttp utilizza CacheInterceptor, che legge Cache-Control dalla risposta e gestisce automaticamente il caching. Se il server ha restituito Cache-Control: max-age=3600, OkHttp non effettuerà una richiesta al server per un'ora. Dopo la scadenza di max-age, OkHttp invia una richiesta condizionale con If-Modified-Since e If-None-Match. Configurazione della cache in OkHttp: OkHttpClient.Builder().cache(Cache(directory, maxSize)).

kotlin
fun createCachedClient(cacheDir: File): OkHttpClient {
    return OkHttpClient.Builder()
        .cache(Cache(cacheDir, 10L * 1024 * 1024))
        .addNetworkInterceptor { chain ->
            val response = chain.proceed(chain.request())
            response.newBuilder()
                .header("Cache-Control",
                    "public, max-age=300")
                .removeHeader("Pragma")
                .build()
        }
        .build()
}

Il codice crea un OkHttpClient con una cache di 10 MB e sovrascrive Cache-Control tramite NetworkInterceptor. Se il server non restituisce Cache-Control o utilizza Expires, l'interceptor aggiunge public, max-age=300 (5 minuti). L'interceptor rimuove l'intestazione obsoleta Pragma (HTTP/1.0) per compatibilità. Il caching su iOS funziona in modo simile tramite URLCache.shared con le impostazioni memoryCapacity e diskCapacity.

Modalità offline e stale-while-revalidate

La direttiva stale-while-revalidate consente di mostrare all'utente una cache obsoleta mentre l'applicazione recupera i dati aggiornati in background. Ciò fornisce un effetto di risposta immediata: l'utente vede il contenuto immediatamente e, dopo un secondo, viene aggiornato alla versione corrente. Supportato da OkHttp a partire dalla versione 3.10 e URLCache su iOS 14+. Esempio: Cache-Control: max-age=3600, stale-while-revalidate=300 — 1 ora di cache aggiornata, poi 5 minuti di visualizzazione di dati obsoleti con aggiornamento in background.

Esempi di configurazione di Cache-Control

Diversi tipi di risorse richiedono diverse strategie di caching. Vediamo le configurazioni ottimali per scenari tipici nello sviluppo mobile. Per i contenuti statici con hash nel nome del file (bundle.abc123.js), puoi impostare max-age fino a 1 anno con immutable. Per gli elenchi API raramente aggiornati (directory, categorie) — max-age da 5 minuti a 1 ora con stale-while-revalidate.

Tipo di risorsaCache-ControlSpiegazione
Risorse statiche versionatepublic, max-age=31536000, immutable1 anno, i file non cambiano (hash nell'URL)
Risorse statiche non versionatepublic, max-age=86400, must-revalidate1 giorno con successiva ricontrollo forzato
API: dati di riferimentopublic, max-age=600, stale-while-revalidate=6010 minuti di cache + 1 minuto obsoleto
API: dati utenteprivate, max-age=601 minuto, solo per un utente specifico
API: dati sensibilino-storeDivieto completo di caching
Pagine HTMLno-cache, must-revalidateConvalida a ogni richiesta, 304 se invariato

È importante ricordare la sicurezza: per le risposte contenenti dati personali dell'utente, imposta sempre private. Senza questa direttiva, un proxy pubblico (ad esempio, aziendale) può memorizzare nella cache la risposta e fornirla a un altro utente. Per i token di autenticazione e le informazioni di pagamento, usa no-store — nemmeno una cache privata dovrebbe memorizzare questi dati sul disco.

Debug della cache

Per verificare la correttezza di Cache-Control, utilizza l'intestazione Age (da quanti secondi la cache è stata memorizzata) e X-Cache (hit/miss sul CDN). Nel browser — la scheda Network, la colonna Size mostra “from disk cache” o “304 Not Modified”. Se una risorsa dovrebbe essere memorizzata nella cache ma viene caricata ogni volta, verifica se il server sta aggiungendo Cache-Control: no-cache o Pragma: no-cache insieme alle tue direttive.

Domande frequenti

Qual è la differenza tra max-age e s-maxage?

max-age si applica a tutte le cache (inclusi i browser), s-maxage si applica solo alle cache condivise (proxy, CDN). Se viene specificato s-maxage, il CDN ignora max-age e utilizza s-maxage. Ciò consente di impostare durate diverse per il browser e il CDN.

Si può annullare il caching dopo aver inviato Cache-Control?

No, dopo aver inviato una risposta con max-age, il client non effettuerà una richiesta fino alla scadenza del timer. Per invalidare immediatamente la cache, devi modificare l'URL della risorsa (aggiungere versione/hash) e inviare notifiche push o messaggi WebSocket per un reset forzato.

Cos'è la direttiva immutable?

La direttiva immutable (RFC 8246) comunica al browser che la risorsa non cambierà mai a questo URL. Il browser non tenta nemmeno di effettuare una richiesta condizionale durante l'aggiornamento della pagina — utilizza la cache fino alla scadenza di max-age. Funziona solo con file versionati.

In che modo Cache-Control influisce sulla SEO?

Googlebot tiene conto di Cache-Control: un caching lungo accelera la scansione ripetuta. noindex con cache veloce è accettabile. no-store può rallentare l'indicizzazione perché Googlebot caricherà la pagina da zero ogni volta. Un max-age troppo breve aumenta il carico del server durante la scansione.

Come configurare Cache-Control in Express.js?

Tramite helmet o middleware: res.set('Cache-Control', 'public, max-age=3600'). Per i file statici, usa express.static con il parametro maxAge: express.static('public', {maxAge: '1y'}). Per le route dinamiche — individualmente in ogni gestore.

Riepilogo

  • Cache-Control — la principale intestazione HTTP per la gestione della cache con un sistema flessibile di direttive
  • max-age — durata in secondi dal momento della risposta; direttiva chiave per tutti gli scenari di caching
  • private vs public — private solo per il client, public per proxy e CDN; influisce sulla sicurezza dei dati
  • no-cache richiede convalida, no-store vieta completamente la cache; scopi diversi, non confondere
  • s-maxage — sovrascrive max-age per le cache condivise, utile per dividere le politiche browser/CDN
  • stale-while-revalidate — mostra cache obsoleta con aggiornamento in background per UX istantanea
  • Raccomandazione — configura Cache-Control per ogni tipo di risorsa sul server e nel client HTTP mobile

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche