Indice dei contenuti dell'articolo:
Nel mondo dell’hosting moderno, la parola “cache” è diventata una specie di mantra. Cache lato server, cache lato CDN, cache nel browser, cache applicativa, cache di pagina, cache di oggetti. Tutto deve essere messo in cache, perché la cache “fa andare veloce il sito”. Ed è vero: se usata bene, la cache è uno degli strumenti più potenti per migliorare le performance percepite, ridurre il carico sui server e aumentare la scalabilità.
Il problema nasce quando il concetto di cache viene semplificato troppo e tradotto in una regola grossolana: “meglio disabilitare tutto, così evitiamo problemi”. È qui che entra in gioco l’uso – e spesso l’abuso – dell’header HTTP Cache-Control, in particolare della direttiva no-store, applicata indiscriminatamente a interi siti web, anche su pagine che non hanno alcuna ragione tecnica per essere escluse da meccanismi di caching moderni.
Questa pratica, molto diffusa soprattutto nel mondo WordPress e più in generale negli stack Apache/PHP tradizionali, ha conseguenze dirette sulle performance, sui Core Web Vitals e su un aspetto spesso ignorato: il funzionamento della Back/Forward Cache dei browser, la cosiddetta BFCache.
Cos’è davvero Cache-Control e perché è così importante
Cache-Control è uno degli header HTTP fondamentali per governare il comportamento delle cache lungo il percorso di una richiesta: browser, proxy intermedi, CDN, reverse proxy e così via. Attraverso direttive come max-age, s-maxage, public, private, no-cache e no-store, il server può indicare come e per quanto tempo una risorsa può essere conservata e riutilizzata.
In teoria, questo sistema permette un controllo molto fine: puoi decidere che una pagina statica sia cacheabile per ore o giorni, che una risorsa personalizzata per utente sia privata, che un endpoint sensibile non venga mai salvato su disco o in memoria. In pratica, però, molti ambienti di hosting e molte configurazioni CMS finiscono per adottare scorciatoie brutali, come l’impostazione globale di Cache-Control: no-store su tutte le risposte HTML.
Il motivo è quasi sempre lo stesso: paura. Paura di servire contenuti sbagliati, paura di mostrare dati di un utente a un altro, paura di gestire correttamente le eccezioni. E così, invece di progettare una strategia di caching sensata, si sceglie la via più semplice: disabilitare tutto.
No-store: cosa significa davvero e perché è così invasivo
La direttiva no-store è una delle più drastiche. Dice esplicitamente a qualsiasi cache: “questa risposta non deve essere memorizzata in alcun modo”. Non in RAM, non su disco, non per un secondo. Il browser deve dimenticarsene immediatamente dopo averla usata per renderizzare la pagina.
Questo ha implicazioni importanti. Prima di tutto, ogni navigazione verso quella pagina comporta una nuova richiesta completa al server, anche se l’utente ha appena visitato la stessa URL un istante prima. Ma c’è di più: no-store disabilita anche alcuni meccanismi avanzati dei browser moderni, tra cui la Back/Forward Cache.
Molti sviluppatori confondono no-store con no-cache, ma sono due cose molto diverse. no-cache non significa “non usare la cache”, significa “devi validare la risorsa prima di riutilizzarla”. In pratica, consente ancora caching con revalidazione, sfruttando ETag o Last-Modified. no-store, invece, taglia completamente fuori qualsiasi forma di memorizzazione.
Usarlo sulle pagine di login, sui checkout, sulle aree personali ha perfettamente senso. Usarlo sulla home page, su un articolo del blog o su una pagina di categoria è, nella maggior parte dei casi, un autogol clamoroso.
Il fenomeno dell’overuse: perché così tanti siti lo fanno
Se guardiamo il panorama reale del web, scopriamo che centinaia di migliaia di siti WordPress (e non solo) inviano ancora oggi Cache-Control: no-store anche su pagine pubbliche e non sensibili. Questo non succede per un singolo motivo, ma per una combinazione di fattori strutturali legati all’ecosistema stesso.
Un primo problema è la qualità media del codice di molti plugin. Una parte significativa dell’ecosistema WordPress è composta da vero e proprio “spaghetti code”, scritto da sviluppatori che spesso hanno solo rudimenti di PHP e MySQL, senza una reale conoscenza del paradigma della programmazione a oggetti, dei principi di progettazione del software o delle implicazioni architetturali delle loro scelte. Non è raro imbattersi in codice che non distingue nemmeno tra tabelle MyISAM e InnoDB, che ignora concetti basilari come transazioni, locking o isolamento, e che tratta il database come un semplice key/value store improvvisato.
In questo contesto, basta un solo plugin scritto male, installato accanto a decine di plugin sviluppati con cura da team professionali, per rompere completamente tutte le best practice sull’uso corretto degli header HTTP, incluso Cache-Control. Un singolo componente può iniziare a inviare header restrittivi, forzare no-store ovunque “per sicurezza” o disabilitare meccanismi di caching fondamentali, trascinando con sé l’intero sito in una configurazione penalizzante dal punto di vista delle performance.
A questo si aggiunge il fatto che molti plugin di cache o di sicurezza scelgono impostazioni ultra-conservative per ridurre il rischio di bug: meglio disabilitare tutto che rischiare di servire una pagina sbagliata. E sullo stesso piano si collocano molti ambienti di hosting condiviso o semi-gestito, che adottano configurazioni standardizzate applicate indistintamente a tutti i siti, senza alcuna reale distinzione tra contenuti statici, semi-dinamici e contenuti realmente sensibili.
Il risultato è amplificato da una cultura tecnica ormai obsoleta, riassumibile nello slogan: “PHP = dinamico = non cacheabile”. Un’idea che era già discutibile dieci anni fa e che oggi è semplicemente anacronistica. Framework moderni, CMS evoluti e reverse proxy come Varnish o Nginx dimostrano quotidianamente che è possibile – e normale – cacheare in modo intelligente anche contenuti generati dinamicamente, purché lo si faccia con criterio, competenza e una reale comprensione di ciò che si sta mettendo in produzione.
Cache-Control e performance: l’impatto sui Core Web Vitals
Quando parliamo di performance oggi non parliamo più solo di “quanto ci mette a caricarsi una pagina”. Parliamo di metriche precise: LCP, CLS, INP, TTFB. Tutte queste metriche sono influenzate, direttamente o indirettamente, dalla possibilità di riutilizzare risorse già ottenute o addirittura pagine già renderizzate.
Se ogni navigazione forza il browser a rifare una richiesta completa al server, con handshake TLS, attesa del backend, generazione HTML e download delle risorse, il TTFB aumenta. Se il browser non può sfruttare cache locali o meccanismi di ripristino istantaneo, l’LCP peggiora. Se ogni “indietro” nel browser comporta un reload completo, l’esperienza utente ne risente in modo evidente.
In questo senso, l’abuso di no-store è una tassa nascosta sulle performance. Non sempre si vede nei test sintetici, ma emerge chiaramente nell’uso reale, soprattutto su dispositivi mobili e connessioni non perfette.
Cos’è la BFCache e perché è così importante per l’esperienza utente
La Back/Forward Cache (BFCache) è un meccanismo implementato da tutti i browser moderni che permette di conservare in memoria lo stato completo di una pagina quando l’utente naviga avanti o indietro nella cronologia. Non viene salvato solo l’HTML, ma l’intero stato della pagina: il DOM già costruito, lo stato JavaScript, la posizione di scroll, i valori dei form e, in generale, tutto ciò che serve per ripristinare la vista esattamente com’era.
Quando l’utente preme “indietro” o “avanti”, invece di rifare una richiesta al server e ricostruire la pagina da zero, il browser ripristina istantaneamente lo snapshot precedentemente memorizzato in memoria. Il risultato è una navigazione percepita come immediata, senza tempi di caricamento, senza sfarfallii dell’interfaccia e senza ricalcoli del layout o riesecuzioni inutili di script.
Per l’utente questa differenza è enorme in termini di reattività e fluidità dell’esperienza. Per il sito, significa meno richieste al server e meno lavoro lato backend e frontend. Per le metriche di performance, infine, la BFCache rappresenta un vantaggio concreto e misurabile, soprattutto nelle interazioni di navigazione interna, dove può azzerare di fatto i tempi di caricamento percepiti.
Il legame tra Cache-Control: no-store e BFCache
Qui arriva il punto critico: una delle condizioni che disabilitano la BFCache è proprio la presenza di Cache-Control: no-store. Se una pagina è marcata come “non memorizzabile in alcun modo”, il browser non può conservarne uno snapshot completo per il ripristino rapido, perché quella direttiva vieta esplicitamente qualsiasi forma di memorizzazione, anche temporanea e solo in memoria.
Il risultato è immediato e misurabile: ogni volta che l’utente torna indietro su quella pagina, il browser è costretto a ricaricarla da zero. Nuova richiesta HTTP, nuovo TTFB, nuovo rendering, nuovo caricamento delle risorse statiche e riesecuzione degli script. Dal punto di vista dell’utente, il sito appare più lento e meno reattivo. Dal punto di vista del server, invece, arrivano più richieste inutili che avrebbero potuto essere evitate grazie al ripristino istantaneo della pagina.
È importante capire che questo non ha nulla a che vedere con la cache HTTP tradizionale delle risorse. La BFCache è un meccanismo interno al browser, pensato esclusivamente per migliorare l’esperienza di navigazione durante l’uso dei pulsanti “indietro” e “avanti”. Disabilitarla senza un vero motivo funzionale o di sicurezza è come togliere il cambio automatico a un’auto moderna “per stare tranquilli”: tecnicamente possibile, ma a scapito del comfort, dell’efficienza e delle prestazioni complessive.
Quando no-store è davvero giustificato
Sia chiaro: no-store non è il male assoluto. È uno strumento necessario in alcuni contesti. Tutte le pagine che mostrano dati sensibili, personali o strettamente legati alla sessione dell’utente dovrebbero usarlo. Pagine di login, aree riservate, carrelli, checkout, pannelli di amministrazione, estratti conto, dati sanitari, tutto ciò che non deve assolutamente essere conservato in alcuna forma.
In questi casi, la perdita della BFCache è un compromesso accettabile, perché la priorità è la sicurezza e la correttezza dei dati. Nessuno vuole che il browser ripristini uno stato obsoleto o mostri informazioni private in modo improprio.
Il problema nasce quando questa direttiva viene applicata in modo indiscriminato anche a pagine pubbliche, statiche o semi-statiche, che non contengono alcuna informazione sensibile e che potrebbero beneficiare enormemente dei meccanismi di caching moderni.
La differenza tra siti SaaS e CMS tradizionali
È interessante notare come molte piattaforme SaaS moderne non soffrano di questo tipo di problemi. Servizi di e-commerce hosted, piattaforme di publishing e web application evolute adottano strategie di caching progettate a livello architetturale, con una chiara separazione tra contenuti pubblici e privati, tra risorse statiche e dinamiche, e tra stato dell’utente e contenuto condiviso. In questi contesti, la cache non è un’aggiunta successiva, ma una parte integrante del design del sistema.
Nel mondo dei CMS tradizionali self-hosted — e WordPress ne è l’esempio più emblematico — si vede invece ancora spesso un approccio “one size fits all”: stessa politica di cache per tutto il sito, stessa diffidenza verso i meccanismi di caching avanzati, stesso risultato penalizzante in termini di performance. Questo non nasce tanto da un limite intrinseco di PHP come linguaggio, quanto dal fatto che WordPress è un progetto nato e cresciuto in larga parte in un contesto amatoriale, dove per anni la priorità è stata la facilità di estensione più che la solidità architetturale.
L’ecosistema che si è formato intorno riflette questa origine: una quantità enorme di plugin sviluppati da programmatori non professionisti, con competenze spesso limitate ai rudimenti di PHP e MySQL, e con una comprensione superficiale di concetti come separazione delle responsabilità, gestione dello stato o progettazione delle policy di caching. In questo scenario, è naturale che molti scelgano la strada più semplice e “sicura” — disabilitare tutto — invece di implementare strategie di cache differenziate e realmente corrette. Il risultato non è un problema strutturale inevitabile, ma la conseguenza diretta di scelte tecniche pigre e di un ecosistema che ha privilegiato la quantità alla qualità.
Cache intelligente: separare ciò che è pubblico da ciò che è sensibile
La chiave per uscire da questa situazione è concettualmente semplice: distinguere. Distinguere tra pagine pubbliche e private, tra contenuti che possono essere cacheati e contenuti che non devono esserlo, tra stato dell’utente e contenuto condiviso.
Una home page, un articolo del blog, una pagina di categoria, una landing page non dovrebbero quasi mai avere no-store. Possono avere policy di cache conservative, possono richiedere revalidazione, possono avere TTL brevi, ma non dovrebbero escludere a priori qualsiasi forma di memorizzazione.
Al contrario, le pagine di account, i carrelli, i flussi di pagamento e le aree amministrative devono essere trattate con maggiore rigidità. È qui che no-store trova il suo posto naturale.
L’impatto reale sull’hosting e sui costi infrastrutturali
C’è anche un aspetto spesso sottovalutato: il costo. Ogni richiesta che potrebbe essere evitata grazie a caching lato browser o BFCache è una richiesta che oggi arriva al server. Su siti con traffico significativo, questo significa più CPU, più RAM, più I/O, più costi di infrastruttura.
Molti hosting provider spendono tempo e risorse per ottimizzare stack, PHP, database e caching server-side, e poi vanificano parte di questi sforzi con policy HTTP eccessivamente restrittive che impediscono al browser di fare la sua parte. È un paradosso abbastanza comune: si ottimizza il backend e si sabotano le performance lato client.
Come valutare se il tuo sito sta penalizzando la BFCache
I browser moderni mettono a disposizione strumenti di sviluppo sempre più evoluti che permettono di analizzare in modo preciso il comportamento delle pagine, inclusa la loro eleggibilità per la BFCache. In Google Chrome, ad esempio, gli strumenti per sviluppatori offrono pannelli e sezioni specifiche che consentono di verificare se una navigazione “back/forward” ha effettivamente utilizzato la BFCache oppure se la pagina è stata ricaricata da zero.
Attraverso il pannello Application e le sezioni dedicate alle performance e alla navigazione, è possibile vedere se la pagina è stata ripristinata dalla BFCache e, soprattutto, quali condizioni ne hanno impedito l’uso. Chrome fornisce indicazioni piuttosto chiare sulle cause di esclusione: presenza di determinati header HTTP, uso di API incompatibili, gestione particolare dello stato della pagina o, molto spesso, proprio l’impostazione di Cache-Control: no-store.
Ed è in questi casi che emerge il paradosso: tra le motivazioni di esclusione compare frequentemente Cache-Control: no-store anche su pagine perfettamente pubbliche e innocue, prive di qualunque stato sensibile o contenuto personalizzato. In altre parole, si scopre che pagine che potrebbero beneficiare pienamente del ripristino istantaneo stanno invece forzando il browser a ricaricare tutto da zero, rinunciando a un’ottimizzazione gratuita e potentissima offerta nativamente dal browser stesso.
Questi strumenti rendono il problema non solo teorico, ma facilmente osservabile e misurabile: basta analizzare una semplice navigazione interna per vedere come una singola direttiva HTTP sbagliata possa annullare completamente i benefici della BFCache, con un impatto diretto sia sull’esperienza utente sia sul carico inutile imposto al server.
Conclusione: meno paura, più strategia
Cache-Control non è un interruttore on/off. È un linguaggio ricco, pensato per descrivere politiche complesse in modo preciso. Usarlo bene significa ottenere siti più veloci, più reattivi, più efficienti e più piacevoli da usare. Usarlo male, o abusarne con no-store ovunque, significa buttare via una parte importante delle ottimizzazioni che i browser moderni offrono gratuitamente.
Il BFCache è uno di questi regali tecnologici: invisibile all’utente, potentissimo nei suoi effetti, ma facile da disattivare con una singola direttiva sbagliata. Ripensare le proprie policy di cache, distinguere tra ciò che è davvero sensibile e ciò che non lo è, è oggi uno dei passi più semplici e più efficaci per migliorare le performance reali di un sito web.
In altre parole: meno paranoia, più progettazione. La cache non è il nemico. L’uso pigro della cache sì.