GuideGuide

Velocizzare WordPress con nginx, Redis e OPcache: guida pratica

Un WordPress lento su un VPS è quasi sempre un WordPress che esegue PHP e interroga il database a ogni visita. La soluzione è mettere tre cache una sopra l'altra: la cache delle pagine di nginx (fastcgi_cache) per i visitatori anonimi, Redis come cache degli oggetti per tutto ciò che resta dinamico e OPcache per non ricompilare PHP a ogni richiesta. Poi si sistemano versione PHP, immagini e database, e si misura il risultato con il TTFB.

In questa guida trovi le configurazioni per Ubuntu 24.04 con nginx e PHP-FPM, i comandi per verificarle e, in fondo, come lo stesso schema è già applicato in Koapanel.

Da dove arriva la lentezza

Quando un visitatore apre una pagina, senza cache succede questo: nginx passa la richiesta a PHP-FPM, WordPress carica core, tema e plugin, esegue decine (a volte centinaia) di query su MariaDB e genera l'HTML. Su un sito con molti plugin sono facilmente diverse centinaia di millisecondi di CPU per ogni pagina, e sotto carico i processi PHP finiscono e le richieste si mettono in coda.

Ogni livello di cache taglia una parte di questo lavoro:

Livello Cosa evita Per chi funziona
Cache delle pagine (nginx) PHP e database, del tutto Visitatori non collegati
Cache degli oggetti (Redis) Molte query ripetute Tutti, anche admin e carrello
OPcache Lettura e compilazione dei file PHP Tutte le richieste PHP

1. Cache delle pagine con nginx fastcgi_cache

È il livello che fa la differenza più grande: una pagina già pronta viene servita da nginx leggendo un file, senza toccare PHP. Non serve un plugin di cache: lo fa nginx con il modulo fastcgi.

Nel contesto http definisci dove salvare la cache. Su Ubuntu i file in /etc/nginx/conf.d/ sono già inclusi dentro http, quindi crea /etc/nginx/conf.d/fastcgi-cache.conf:

fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=WORDPRESS:100m max_size=1g inactive=60m use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

Poi, nel blocco server del sito, decidi cosa non mettere in cache e attiva la cache nella location PHP:

set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin/|/wp-json/|/xmlrpc.php|wp-.*\.php|/feed/|sitemap(_index)?\.xml|/cart/|/checkout/|/my-account/") { set $skip_cache 1; }
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in|woocommerce_items_in_cart|woocommerce_cart_hash") { set $skip_cache 1; }
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 301 302 10m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_background_update on;
fastcgi_cache_lock on;
add_header X-Cache $upstream_cache_status;
}

Controlla la sintassi, ricarica e verifica l'intestazione X-Cache: la prima richiesta dà MISS, la seconda HIT.

sudo nginx -t && sudo systemctl reload nginx
curl -sI https://www.esempio.it/ | grep -i x-cache
curl -sI https://www.esempio.it/ | grep -i x-cache

Quanto deve durare la cache

Il problema di ogni cache delle pagine è l'invalidazione: pubblichi un articolo e la home in cache mostra ancora quello vecchio. La versione open source di nginx non ha un comando per cancellare una singola pagina; hai due strade:

  • durata breve (da 30 secondi a qualche minuto, la cosiddetta microcache): i contenuti si aggiornano da soli e sotto carico quasi tutte le richieste restano servite dalla cache;
  • durata lunga e svuotamento manuale (o da un plugin che cancella i file della cache) quando pubblichi.

Per svuotare tutto a mano:

sudo find /var/cache/nginx/wordpress -type f -delete

Per la maggior parte dei siti la durata breve è la scelta più sicura: nessun contenuto vecchio visibile a lungo, nessuna dipendenza da plugin.

2. Cache degli oggetti con Redis

La cache delle pagine non aiuta chi è collegato, chi ha il carrello pieno o le chiamate a wp-admin. Qui entra Redis: WordPress salva in memoria i risultati delle query e le opzioni, invece di chiederli a MariaDB ogni volta.

sudo apt install redis-server php8.3-redis
sudo systemctl restart php8.3-fpm

Limita la memoria di Redis e fagli scartare le chiavi meno usate quando è pieno, in /etc/redis/redis.conf:

maxmemory 256mb
maxmemory-policy allkeys-lru

Per una cache non serve salvare su disco: puoi disattivare i salvataggi commentando le righe save o impostando save "". Poi sudo systemctl restart redis-server.

In wp-config.php, prima della riga «That's all, stop editing!», indica dove si trova Redis e un prefisso diverso per ogni sito, così più siti non si sovrascrivono le chiavi:

define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_PREFIX', 'esempio_it:' );

Installa e attiva il plugin Redis Object Cache con WP-CLI, eseguito come utente del sito (qui www-data):

sudo -u www-data wp --path=/var/www/esempio.it plugin install redis-cache --activate
sudo -u www-data wp --path=/var/www/esempio.it redis enable
sudo -u www-data wp --path=/var/www/esempio.it redis status

Una nota di sicurezza: un solo Redis condiviso tra siti di clienti diversi permette a un sito di leggere le chiavi degli altri. Su un server con più clienti è meglio un'istanza per sito, raggiungibile solo da un socket locale.

3. OPcache

OPcache tiene in memoria il codice PHP già compilato. Su Ubuntu è già installata e attiva, ma i valori predefiniti sono piccoli per WordPress con molti plugin. Crea /etc/php/8.3/fpm/conf.d/99-opcache.ini:

opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
sudo systemctl restart php8.3-fpm

Con revalidate_freq=60 PHP controlla se i file sono cambiati al massimo una volta al minuto: dopo un aggiornamento manuale di un file il cambiamento può comparire con un minuto di ritardo. Il JIT di PHP 8 serve soprattutto per calcoli pesanti; su WordPress, che passa gran parte del tempo ad attendere database e rete, di solito non cambia molto.

4. Versione PHP e PHP-FPM

Ogni nuova versione di PHP è in genere più veloce della precedente, e le versioni vecchie non ricevono più patch di sicurezza. WordPress consiglia PHP 8.3 o superiore e MariaDB 10.11 o superiore. Secondo il calendario ufficiale di PHP, la 8.2 riceve patch di sicurezza solo fino al 31 dicembre 2026. Ubuntu 24.04 include PHP 8.3; le versioni 8.4 e 8.5 si installano dall'archivio ondrej/php. Prima di cambiare versione, prova il sito su una copia: qualche plugin datato può dare errori.

Controlla anche il pool di PHP-FPM (/etc/php/8.3/fpm/pool.d/www.conf): pm.max_children va calcolato sulla RAM libera divisa per la memoria media di un processo PHP (spesso 60-100 MB con WordPress). Troppo basso e le richieste si accodano, troppo alto e il server va in swap.

5. Immagini e file statici

Le immagini sono spesso la parte più pesante della pagina e incidono sui Core Web Vitals più del TTFB.

  • Carica immagini già ridimensionate e usa formati moderni: WordPress supporta WebP e, dalla 6.5, anche AVIF.
  • Il caricamento differito (loading="lazy") è già automatico in WordPress per le immagini sotto la parte visibile.
  • Fai mettere in cache ai browser i file statici:
location ~* \.(?:css|js|jpg|jpeg|gif|png|webp|avif|svg|ico|woff2)$ {
expires 30d;
access_log off;
try_files $uri =404;
}
  • Se il pubblico è lontano dal server, una CDN davanti al sito accorcia la distanza per immagini, CSS e JavaScript.

6. Database

  • Opzioni caricate automaticamente: plugin disinstallati lasciano spesso dati in wp_options che vengono letti a ogni pagina. Controlla quanti byte pesano:
sudo -u www-data wp --path=/var/www/esempio.it option list --autoload=on --format=total_bytes
  • Revisioni: limita quelle conservate con define( 'WP_POST_REVISIONS', 10 ); in wp-config.php.
  • Transient scaduti: wp transient delete --expired.
  • WP-Cron: di serie gira durante le visite. Meglio disattivarlo con define( 'DISABLE_WP_CRON', true ); e lanciarlo dal cron di sistema ogni 5 minuti.
  • MariaDB: innodb_buffer_pool_size in /etc/mysql/mariadb.conf.d/50-server.cnf dovrebbe contenere i dati usati più spesso; su un VPS che ospita anche PHP, una fetta ragionevole della RAM, non tutta.
  • Per trovare le query lente usa il plugin Query Monitor o lo slow query log di MariaDB.

Come misurare: TTFB e PageSpeed

Misura prima e dopo, sempre da un browser o da un computer non collegato a wp-admin (gli utenti collegati saltano la cache delle pagine).

Il TTFB (tempo al primo byte) dice quanto ci mette il server a rispondere. Con curl:

for i in 1 2 3 4 5; do curl -o /dev/null -s -w '%{time_starttransfer}\n' https://www.esempio.it/; done

Guarda la mediana, non il singolo valore. Secondo web.dev un TTFB buono è di 0,8 secondi o meno; con la cache delle pagine attiva su un server vicino si scende di solito molto sotto.

PageSpeed Insights misura l'esperienza completa: LCP, CLS, INP. Distingui i dati di laboratorio (una prova simulata) da quelli reali degli utenti Chrome, che compaiono solo per siti con abbastanza traffico. Se il TTFB è buono ma il punteggio resta basso, il problema è di solito nel front-end: immagini, font, JavaScript di terze parti.

Come lo fa Koapanel

Se non vuoi mantenere a mano questi file su ogni sito, Koapanel applica lo stesso schema dalla sezione WordPress (documentazione):

  • Cache delle pagine nginx già configurata, con durata predefinita di 60 secondi. Non vengono mai messe in cache richieste POST, indirizzi con parametri, utenti collegati, carrello, checkout e account (anche WooCommerce), area admin, REST API, feed e sitemap. Il pulsante Svuota tutte cancella la cache del sito e la percentuale di hit è calcolata sul registro di accesso delle ultime 24 ore.
  • Redis privato per ogni sito, raggiungibile solo su un socket locale (nessuna porta di rete), con memoria limitata (64 MB di serie) e senza salvataggi su disco; il plugin Redis Object Cache viene installato e attivato dal pannello.
  • OPcache attiva per tutto il server, con hit rate e memoria della versione PHP del sito visibili nel pannello.
  • Misura del TTFB della home con e senza cache (mediana di 5 misure), con lo storico delle misure.
  • PHP dalla 7.4 alla 8.5, scelto per ogni sito, con memoria e limiti regolabili sito per sito (versioni PHP).
  • WP-Cron sul cron di sistema, ogni 5 minuti.

Cosa non c'è: Koapanel usa solo nginx, quindi niente Apache e regole .htaccess, e niente LiteSpeed con LSCache. Se il tuo stack è già basato su LiteSpeed e ne sei soddisfatto, LSCache è un'ottima alternativa a questo schema. Per mettere in piedi il server da zero trovi la guida WordPress su VPS.

Domande frequenti

Serve un plugin di cache se uso nginx fastcgi_cache?

No per la cache delle pagine: la gestisce nginx. Un plugin può servire per ottimizzare CSS, JavaScript e immagini, o per svuotare la cache quando pubblichi.

Redis sostituisce la cache delle pagine?

No, sono complementari. La cache delle pagine evita del tutto PHP per i visitatori anonimi; Redis accelera le pagine che devono comunque passare da PHP, come wp-admin e il carrello.

La cache delle pagine rompe WooCommerce?

Non se escludi carrello, checkout, account e i cookie di WooCommerce, come nell'esempio sopra. Prova sempre un ordine completo dopo averla attivata.

Quale versione di PHP scegliere?

Una supportata e compatibile con i tuoi plugin: oggi 8.3 o 8.4 sono scelte sicure, 8.5 è la più recente. Prova prima su una copia del sito.

Perché PageSpeed resta basso anche con il TTFB buono?

Perché PageSpeed misura anche il caricamento nel browser: immagini grandi, font e script esterni pesano più del server.

Prova la configurazione già pronta

Puoi installare Koapanel gratis su un Ubuntu 24.04 nuovo (fino a 3 siti per uso personale) e confrontare il TTFB prima e dopo dalla scheda WordPress, oppure provare la demo pubblica su https://demo.koapanel.app/. Piani e limiti sono nella pagina prezzi.

Prova Koapanel sul tuo server

Un comando su Ubuntu 24.04, gratuito fino a 3 siti. Sei un provider o un'agenzia? Parliamo di prezzi all'ingrosso e migrazioni.

Altre guide