Vai al contenuto

Cache WordPress sotto carico: chiavi, invalidazione e richieste a PHP

Il giorno che la cache di WordPress non è più bastata (45M visite/mese)

Una cache WordPress può essere attiva e lasciare comunque arrivare quasi tutte le richieste a PHP. Succede quando richieste che producono la stessa pagina vengono trattate come oggetti diversi. Prima di aggiungere memoria o un’altra cache, controllerei quali chiavi vengono create durante il traffico reale.

Un incidente documentato il 6 settembre 2026 nella nostra infrastruttura editoriale mostra questo caso: link condivisi su Facebook portavano valori diversi del parametro fbclid. La cache nginx includeva l’intera query string nella chiave, così molti click sulla stessa pagina producevano nuovi miss.

Seguire la saturazione dal proxy al processo

Nei log dell’incidente, i processi PHP erano impegnati e nginx segnalava l’esaurimento delle connessioni disponibili verso gli upstream. Il database non mostrava lo stesso carico. La diagnosi ha quindi collegato frammentazione della cache, richieste a PHP e limite nel proxy.

Il problema coinvolgeva anche altri siti sullo stesso ambiente perché alcune risorse erano condivise. Un numero di richieste apparentemente modesto può essere sufficiente se ciascuna richiede lavoro costoso e si accumula in attesa.

Per ricostruire il percorso guarderei stato della cache, query string, tempi upstream, coda PHP-FPM e messaggi nginx nella stessa finestra. Il solo numero di visite mensili non descrive il picco né il costo della singola richiesta.

Normalizzare soltanto ciò che non cambia il contenuto

Nel caso documentato, la correzione ha separato gli indirizzi con soli parametri di tracciamento da quelli con parametri funzionali. I primi possono condividere la copia della pagina quando quei parametri non modificano la risposta dell’applicazione.

Non eliminerei tutta la query string per principio. Ricerca, filtri, lingua, paginazione, anteprime e token possono cambiare contenuto o autorizzazione. Due richieste devono condividere la cache soltanto quando è corretto condividere la risposta.

Controllerei anche le variabili nginx usate: $request_uri conserva l’URI originale con gli argomenti, mentre $uri può cambiare dopo una riscrittura interna. Se più pagine vengono instradate verso index.php, usare ingenuamente l’URI riscritto può creare collisioni tra articoli diversi.

Provare le chiavi con richieste rappresentative

Preparerei una matrice con pagina normale, stessa pagina con tracking, ricerca, utente autenticato e anteprima. Per ogni caso confronterei stato della cache e corpo ricevuto. Un hit rapido sulla risposta sbagliata è un errore funzionale.

Nella documentazione FastCGI di nginx, bypass della lettura e condizioni che impediscono il salvataggio hanno direttive distinte. Una richiesta privata non deve soltanto evitare la copia esistente: la sua risposta non deve diventare una copia pubblica.

Verificherei cookie, header e risposte con Set-Cookie. Le regole devono riflettere le funzionalità del sito e i plugin attivi, inclusi eventuali acquisti o contenuti riservati.

Cache di pagina e cache degli oggetti hanno costi diversi

La object cache WordPress conserva risultati riutilizzabili dall’applicazione. Una cache della pagina può evitare di eseguire PHP per una risposta già pronta. OPcache conserva invece bytecode PHP, non l’HTML finale.

Questi livelli possono convivere, ma i loro hit rate non si sommano in una percentuale significativa senza definire richieste e denominatori. Guarderei quale lavoro viene evitato a ciascun livello e quale rimane sulle richieste non memorizzabili.

Nel runbook dell’incidente è emerso anche l’effetto del reload PHP-FPM sulla cache APCu: la rigenerazione successiva può aumentare temporaneamente il carico. Durante un intervento valuterei l’ordine delle operazioni e la capacità disponibile per il riscaldamento.

Invalidazione e contenuti scaduti fanno parte del servizio

Definirei quali pagine cambiano quando viene pubblicato o corretto un articolo: il pezzo, la home, alcuni archivi e i feed. Dopo l’invalidazione controllerei la versione ricevuta da un visitatore anonimo.

Cache lock e uso di contenuti stale possono limitare richieste simultanee al backend in condizioni configurate. Richiedono comunque una scelta sulla durata accettabile della copia vecchia e una prova del comportamento a scadenza.

Per il purge locale eliminerei soltanto i file della cache del dominio interessato, conservando le directory delle zone nginx. Cancellare la struttura può impedire al servizio di ricreare correttamente gli oggetti.

La verifica finale comprende anche i miss

Dopo la modifica proverei picco, scadenza e pubblicazione, insieme a una richiesta che deve arrivare a PHP. La cache riduce il lavoro ripetuto, ma il backend deve continuare a gestire login, scritture e contenuti non memorizzabili.

Il risultato da cercare è una risposta corretta con un costo prevedibile. Nel caso dei link con tracking, capire perché la stessa pagina occupava chiavi differenti ha fornito una correzione più precisa del semplice aumento dei worker.