Perché Varnish per WordPress
Varnish è un HTTP accelerator che si posiziona davanti al web server (Nginx o Apache) e serve contenuti cachati direttamente dalla RAM. A differenza di soluzioni PHP-based come WP Super Cache o W3 Total Cache, Varnish intercetta le richieste prima che raggiungano il web server, riducendo drasticamente il carico sul backend.
In deployment reali con traffico medio-alto (50.000+ pageviews/giorno), Varnish può ridurre il Time to First Byte da 200-400ms a 5-20ms, con hit rate superiori al 95% se configurato correttamente. Il problema è che WordPress genera contenuti dinamici e richiede una strategia di invalidazione precisa per evitare di servire contenuti obsoleti.
Architettura e flusso delle richieste
La stack tipica con Varnish è la seguente:
- Client → Varnish (porta 80/443) → Nginx/Apache (porta 8080) → PHP-FPM → WordPress
- Varnish gestisce SSL tramite Hitch o delegando a Nginx in reverse proxy
- Le richieste autenticate (logged-in users) bypassano Varnish usando cookie detection
- I purge vengono inviati da WordPress a Varnish via HTTP PURGE method
Il vantaggio principale: Varnish serve contenuti statici a velocità di rete senza toccare PHP. Lo svantaggio: configurazione più complessa e necessità di gestire l’invalidazione cache a livello applicativo.
Installazione e configurazione base
Su Ubuntu 22.04/24.04, l’installazione è diretta:
apt update
apt install varnish
systemctl enable varnish
Modifica /etc/default/varnish per impostare la porta di ascolto:
DAEMON_OPTS="-a :80 \
-T localhost:6082 \
-f /etc/varnish/default.vcl \
-s malloc,2G"
Configurazione chiave:
- -a :80: Varnish ascolta sulla porta 80 (HTTP pubblico)
- -T localhost:6082: interfaccia admin per varnishadm
- -s malloc,2G: alloca 2GB di RAM per la cache (adatta in base al server)
- -f default.vcl: file di configurazione principale
Sposta Nginx/Apache sulla porta 8080 modificando la configurazione del virtual host, poi riavvia entrambi i servizi.
VCL personalizzata per WordPress
Il file /etc/varnish/default.vcl definisce la logica di caching. Configurazione base ottimizzata per WordPress:
vcl 4.1;
backend default {
.host = "127.0.0.1";
.port = "8080";
.connect_timeout = 600s;
.first_byte_timeout = 600s;
.between_bytes_timeout = 600s;
}
acl purge {
"localhost";
"127.0.0.1";
}
sub vcl_recv {
# Rimuovi parametri analytics
if (req.url ~ "(\?|&)(utm_source|utm_medium|utm_campaign|gclid|fbclid)=") {
set req.url = regsub(req.url, "&(utm_source|utm_medium|utm_campaign|gclid|fbclid)=([A-z0-9_\-\.\%25]+)", "");
set req.url = regsub(req.url, "\?(utm_source|utm_medium|utm_campaign|gclid|fbclid)=([A-z0-9_\-\.\%25]+)", "?");
set req.url = regsub(req.url, "\?&", "?");
set req.url = regsub(req.url, "\?$", "");
}
# Gestione PURGE
if (req.method == "PURGE") {
if (!client.ip ~ purge) {
return(synth(405, "Not allowed."));
}
return(purge);
}
# Bypass per utenti loggati
if (req.http.Cookie ~ "wordpress_logged_in|wp-postpass|comment_author") {
return(pass);
}
# Bypass per admin e login
if (req.url ~ "^/wp-(admin|login|cron)") {
return(pass);
}
# Rimuovi cookie non necessari
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(__[a-z]+|has_js)=[^;]*", "");
set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");
if (req.http.Cookie == "") {
unset req.http.Cookie;
}
}
sub vcl_backend_response {
# Cache per 24 ore di default
set beresp.ttl = 24h;
set beresp.grace = 1h;
# Non cachare errori
if (beresp.status >= 500) {
set beresp.ttl = 0s;
}
}
sub vcl_deliver {
# Header di debug
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
set resp.http.X-Cache-Hits = obj.hits;
}
Elementi critici della configurazione:
- Normalizzazione URL: rimuove parametri tracciamento per aumentare hit rate
- Cookie detection: bypass automatico per utenti autenticati
- ACL purge: solo localhost può inviare richieste PURGE
- Grace mode: serve contenuti stale se backend è down
Plugin WordPress per invalidazione automatica
WordPress necessita di un plugin per comunicare con Varnish e invalidare la cache quando i contenuti cambiano. Le opzioni principali sono:
Varnish HTTP Purge (ufficiale)
Plugin leggero sviluppato dal team WordPress.com VIP. Supporta purge automatico su:
- Pubblicazione/aggiornamento post
- Nuovi commenti approvati
- Aggiornamenti temi e plugin
- Cambio impostazioni permalink
Configurazione in wp-config.php:
define('VHP_VARNISH_IP', '127.0.0.1');
Il plugin invia richieste PURGE all’IP specificato. Per installazioni multi-server, configura array di IP Varnish.
Proxy Cache Purge
Alternativa più configurabile, supporta patterns di purge personalizzati e wildcard. Utile per architetture complesse con più layer di cache.
Strategie di invalidazione avanzate
Per siti complessi, il purge totale ad ogni modifica non è efficiente. Strategie granulari:
Purge selettivo per taxonomy
Quando aggiorni un post nella categoria “Guide”, invalida solo:
- URL del singolo post
- Pagina archivio categoria
- Homepage (se mostra post recenti)
- Feed RSS
Implementazione custom con hook WordPress:
add_action('save_post', function($post_id) {
$post = get_post($post_id);
$urls = [
get_permalink($post_id),
home_url('/'),
];
$categories = get_the_category($post_id);
foreach ($categories as $cat) {
$urls[] = get_category_link($cat->term_id);
}
foreach ($urls as $url) {
wp_remote_request($url, [
'method' => 'PURGE',
'headers' => ['Host' => parse_url($url, PHP_URL_HOST)]
]);
}
});
Cache tagging (Varnish Plus)
Varnish Plus (versione commerciale) supporta surrogate keys per invalidazione basata su tag. WordPress può aggiungere header Surrogate-Key con ID post, categorie, autori, e Varnish invalida tutte le pagine con quel tag.
Monitoraggio e debugging
Strumenti essenziali per verificare il corretto funzionamento:
varnishstat
Statistiche real-time sul hit rate:
varnishstat -1 | grep cache_hit
Target: cache_hit rate > 90% per siti con traffico consolidato.
varnishlog
Log dettagliato delle richieste:
varnishlog -q "ReqURL ~ '^/blog/'"
Filtra per specifici pattern URL per debug mirato.
Header HTTP
Verifica gli header di risposta con curl:
curl -I https://tuosito.it | grep X-Cache
Risposta attesa: X-Cache: HIT per contenuti cachati.
Problemi comuni e soluzioni
Scenari tipici in deployment reali:
- Hit rate basso (<70%): verifica normalizzazione cookie e URL. Spesso causato da query string dinamiche o cookie di terze parti (analytics, chat widget)
- Contenuti stale dopo update: controlla che il plugin purge sia attivo e che l’IP Varnish sia corretto. Verifica ACL purge in VCL
- Problemi con SSL: Varnish non gestisce nativamente HTTPS. Usa Nginx come SSL terminator davanti a Varnish o configura Hitch
- Cache di pagine 404: aggiungi check su
beresp.statusinvcl_backend_responseper non cachare errori
Per siti ecommerce WooCommerce, bypassa completamente cart, checkout e my-account aggiungendo in vcl_recv:
if (req.url ~ "^/(cart|checkout|my-account)") {
return(pass);
}
Performance reali: case study
Dati da deployment AgencyPilot su client medio (WordPress + WooCommerce, 80.000 pageviews/mese):
- Prima di Varnish: TTFB medio 340ms, server load 2.8, 4GB RAM utilizzata
- Dopo Varnish: TTFB medio 18ms (cached), server load 0.6, 2.5GB RAM utilizzata (+ 2GB Varnish)
- Hit rate stabile: 94.3% dopo ottimizzazione VCL e normalizzazione cookie
ROI significativo per siti con traffico >30.000 pageviews/mese. Sotto quella soglia, soluzioni più semplici come Redis Object Cache o FastCGI Cache possono essere sufficienti.
FAQ
Varnish è compatibile con tutti i plugin WordPress?
La maggior parte sì, ma plugin che richiedono contenuti sempre freschi (forum real-time, live chat, countdown dinamici) possono avere problemi. Soluzione: usa AJAX per caricare quei componenti client-side, bypassando la cache. Per ecommerce, configura bypass esplicito per cart e checkout come mostrato nella guida.
Quanto RAM devo allocare a Varnish?
Dipende dal numero di pagine uniche e dalla dimensione media. Formula approssimativa: (pagine uniche × 50KB) × 1.5 di overhead. Per un blog con 1000 pagine indicizzate: ~75MB teorici, ma alloca almeno 512MB-1GB per buffering. Siti con molte immagini inline o CSS/JS pesanti: 2-4GB. Monitora con varnishstat il valore n_lru_nuked (oggetti rimossi per mancanza spazio): se cresce rapidamente, aumenta la RAM.
Come gestisco ambienti staging con Varnish?
Configura virtual host separati in VCL usando req.http.host. Esempio per staging su sottodominio:
if (req.http.host ~ "staging\.tuosito\.it") {
set req.backend_hint = staging_backend;
return(pass); # Bypass cache su staging
}
Alternativa: usa Varnish solo in produzione e FastCGI cache su staging per semplificare il workflow di test.
Posso usare Varnish con managed hosting WordPress?
Dipende dal provider. Kinsta, WP Engine e Flywheel usano soluzioni proprietarie (edge cache, EverCache) e non permettono Varnish custom. Su VPS gestiti (Cloudways, SpinupWP) o server dedicati hai controllo completo. Se usi managed hosting, valuta se la loro cache integrata è sufficiente prima di migrare su infrastruttura custom.
Varnish sostituisce CDN come Cloudflare?
No, sono complementari. Varnish cacha a livello server origin, CDN distribuisce contenuti su edge location globali. Stack ideale: Cloudflare (edge cache + DDoS protection) → Varnish (full page cache origin) → Nginx → WordPress. Cloudflare serve asset statici (immagini, CSS, JS) e prima richiesta HTML, Varnish cacha la generazione dinamica lato server. Disattiva HTML caching su Cloudflare se usi Varnish per evitare doppia cache e problemi di invalidazione.