Perché Nginx e PHP-FPM per WordPress
La combinazione Nginx + PHP-FPM rappresenta lo standard de facto per installazioni WordPress ad alte prestazioni. A differenza di Apache con mod_php, questa architettura separa il web server dal processore PHP, permettendo un controllo granulare su risorse, isolamento e performance.
I vantaggi principali per le agenzie che gestiscono portfolio client:
- Isolamento per singolo sito tramite pool PHP-FPM dedicati
- Gestione precisa della memoria e dei processi PHP
- Possibilità di differenziare versioni PHP per client
- Logging e monitoring separati per troubleshooting rapido
- Ottimizzazione cache a livello applicativo (opcache, APCu)
Su server gestiti con AgencyPilot, questa configurazione permette di scalare fino a 50-100 siti WordPress su singolo VPS mantenendo performance stabili sotto carico.
Configurazione PHP-FPM: pool dedicati per client
La prima regola per gestire siti multipli è creare pool PHP-FPM separati. Questo previene che un sito compromesso o sotto attacco consumi risorse degli altri.
Struttura pool per singolo sito
File /etc/php/8.3/fpm/pool.d/cliente-sito.conf:
[cliente-sito] user = cliente-sito group = cliente-sito listen = /run/php/php8.3-fpm-cliente-sito.sock listen.owner = www-data listen.group = www-data listen.mode = 0660 pm = ondemand pm.max_children = 10 pm.process_idle_timeout = 10s pm.max_requests = 500 php_admin_value[error_log] = /var/log/php-fpm/cliente-sito-error.log php_admin_flag[log_errors] = on php_admin_value[memory_limit] = 256M php_admin_value[upload_max_filesize] = 64M php_admin_value[post_max_size] = 64M php_admin_value[max_execution_time] = 60
Parametri chiave da configurare:
- pm = ondemand: avvia processi solo quando necessario, ideale per siti con traffico variabile
- pm.max_children: limite processi simultanei. Formula base: (RAM disponibile per sito / 128MB). Per sito medio: 8-10 processi
- pm.process_idle_timeout: tempo prima di terminare processi inattivi. 10-30s per bilanciare risorse e latenza
- pm.max_requests: riavvia processo dopo N richieste per prevenire memory leak. 500-1000 per WordPress
Process manager: pm dynamic vs ondemand
Per siti ad alto traffico (>10k visite/giorno), considera pm = dynamic:
pm = dynamic pm.max_children = 20 pm.start_servers = 4 pm.min_spare_servers = 2 pm.max_spare_servers = 8
Questo mantiene processi pre-forked pronti, riducendo latenza su picchi di traffico. Test A/B su installazioni reali mostrano riduzione TTFB medio del 15-20% sotto carico.
Ottimizzazione Nginx per WordPress
La configurazione Nginx deve gestire routing, caching e sicurezza senza appesantire PHP-FPM con richieste statiche.
Server block ottimizzato
server {
listen 443 ssl http2;
server_name esempio.it www.esempio.it;
root /var/www/esempio.it/public;
index index.php;
access_log /var/log/nginx/esempio.it-access.log combined buffer=32k flush=5s;
error_log /var/log/nginx/esempio.it-error.log warn;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Cache statico aggressivo
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 365d;
add_header Cache-Control "public, immutable";
access_log off;
}
# Block WordPress files sensibili
location ~ /\.(ht|git|svn) { deny all; }
location = /xmlrpc.php { deny all; }
location = /wp-config.php { deny all; }
location ~ ^/wp-content/uploads/.*\.php$ { deny all; }
# WordPress permalink
location / {
try_files $uri $uri/ /index.php?$args;
}
# FastCGI PHP
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/run/php/php8.3-fpm-esempio.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# FastCGI cache (opzionale, vedi sotto)
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 60m;
add_header X-FastCGI-Cache $upstream_cache_status;
}
}
FastCGI cache: quando implementarla
Per siti senza ecommerce o aree membri, FastCGI cache offre performance superiori a qualsiasi plugin cache PHP:
# In http block di nginx.conf
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=WORDPRESS:100m inactive=60m max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
# In server block
set $skip_cache 0;
# POST requests e query string
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
# URIs WordPress da non cachare
if ($request_uri ~* "/wp-admin/|/wp-json/|/wp-login.php|/cart/|/checkout/|/my-account/") {
set $skip_cache 1;
}
# Cookie logged in
if ($http_cookie ~* "wordpress_logged_in|comment_author|wp_postpass") {
set $skip_cache 1;
}
Risultati reali: TTFB da 300-500ms a 5-15ms per hit cache. Richieste PHP ridotte del 90-95% su contenuti statici.
Tuning PHP per WordPress
Le impostazioni PHP di default sono raramente ottimali per WordPress in produzione.
Opcache: configurazione produzione
File /etc/php/8.3/mods-available/opcache.ini:
opcache.enable=1 opcache.memory_consumption=256 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=20000 opcache.revalidate_freq=60 opcache.fast_shutdown=1 opcache.enable_cli=0 ; Validazione timestamp (development: 1, production: 0) opcache.validate_timestamps=0 ; JIT PHP 8+ (conservative per WordPress) opcache.jit=1255 opcache.jit_buffer_size=128M
Note importanti:
- opcache.validate_timestamps=0: disabilita check modifiche file. Richiede reload PHP-FPM dopo deploy, ma elimina overhead filesystem
- opcache.max_accelerated_files: WordPress + plugin/tema tipico = 5000-10000 file. Imposta 20000 per margine
- opcache.jit_buffer_size: JIT in PHP 8.1+ migliora performance calcoli pesanti. Test su Gutenberg: ~10% miglioramento rendering editor
Limiti memoria e execution time
Per WordPress moderno con page builder:
- memory_limit: 256MB standard, 512MB per editor complessi (Elementor, Divi)
- max_execution_time: 60s base, 120s per import/export o build cache
- upload_max_filesize: 64-128MB per gestire media HD e import tema
Imposta questi valori nel pool PHP-FPM specifico, non in php.ini globale.
Monitoring e troubleshooting
Senza dati reali, ottimizzare è tirare a indovinare. Configura monitoring fin dall’inizio.
Metriche chiave da tracciare
Abilita status page PHP-FPM in /etc/php/8.3/fpm/pool.d/cliente-sito.conf:
pm.status_path = /fpm-status ping.path = /fpm-ping
Poi in Nginx server block:
location ~ ^/(fpm-status|fpm-ping)$ {
access_log off;
allow 127.0.0.1;
deny all;
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm-cliente-sito.sock;
}
Monitora con script cron o tool come Netdata, Prometheus:
- active processes: se costantemente = max_children, aumenta limite o ottimizza query
- slow requests: identifica script lenti (abilita
request_slowlog_timeout = 5snel pool) - listen queue: se >0 costante, processi insufficienti per traffico
Slow log per debug performance
Nel pool PHP-FPM:
slowlog = /var/log/php-fpm/cliente-sito-slow.log request_slowlog_timeout = 5s request_terminate_timeout = 120s
Analizza slow log periodicamente per identificare plugin problematici o query database non ottimizzate.
Checklist deployment produzione
Prima di mettere in produzione una configurazione Nginx + PHP-FPM:
- Verifica permessi file: PHP-FPM user deve leggere codice WordPress (chmod 755 directory, 644 file)
- Test configurazione:
nginx -tephp-fpm8.3 -t - Abilita log rotazione per evitare disk full
- Configura opcache reset su deploy (script o webhook)
- Testa sotto carico con
abok6prima di traffico reale - Monitora error log prima 24h per errori configurazione
- Backup configurazione working: versionamento config con Git
Per agenzie con dashboard AgencyPilot, template configurazione possono essere applicati automaticamente a nuovi siti client, riducendo setup da ore a minuti.
FAQ
Quanta RAM serve per pool PHP-FPM per singolo sito WordPress?
Un sito WordPress medio richiede 128-256MB per processo PHP. Con pm.max_children = 10, riserva 1.5-2GB RAM. Per server multi-tenant, calcola: (numero siti × 2GB) + 2GB sistema. VPS da 8GB gestisce comodamente 3-4 siti medi o 10-15 siti piccoli.
PHP-FPM ondemand vs dynamic: quale scegliere?
Usa ondemand per siti con traffico <10k visite/giorno o irregolare: risparmia RAM avviando processi solo quando necessari. Scegli dynamic per siti ad alto traffico costante (>20k visite/giorno): mantiene processi pre-forked riducendo latenza del 15-20% su picchi.
FastCGI cache o plugin cache come WP Rocket?
FastCGI cache è più veloce (TTFB 5-15ms vs 50-100ms plugin) perché bypassa completamente PHP. Ma richiede gestione purge via Nginx e non supporta logiche cache avanzate. Plugin cache sono più flessibili per ecommerce o aree membri. Per blog/corporate senza login, FastCGI cache è superiore.
Come calcolare pm.max_children ottimale?
Formula: pm.max_children = RAM_disponibile / memoria_media_processo. Memoria processo varia 80-200MB per WordPress. Verifica con: ps aux | grep php-fpm sotto carico. Per sito tipico su server dedicato: inizia con 15-20, monitora uso RAM e slow request, aggiusta. Mai superare RAM fisica disponibile.
Opcache validate_timestamps=0 rompe development?
Sì, in development tieni validate_timestamps=1 (default) così modifiche file sono immediate. In produzione imposta =0 per performance migliori. Gestisci con php.ini separati per ambiente o variabile in pool: php_admin_value[opcache.validate_timestamps] = 0 solo in pool produzione.