Sur un site PHP, le temps de réponse du serveur se joue à trois niveaux : PHP compile-t-il les scripts à chaque requête ? Y a-t-il assez de processus PHP pour les visiteurs simultanés ? Et surtout, a-t-on besoin d'exécuter PHP pour chaque page ? Ce guide règle les trois, dans cet ordre. Les exemples visent WordPress sur Ubuntu 24.04 avec PHP 8.3, mais s'appliquent à Laravel ou à tout autre application PHP.
1. Mesurer avant de régler
Sans mesure de départ, impossible de savoir si un réglage a servi. Relevez le temps de réponse d'une page représentative, trois fois de suite :
for i in 1 2 3; do
curl -o /dev/null -s -w "TTFB %{time_starttransfer}s\n" https://monsite.fr/
done
Au-delà de 800 ms, Google considère le temps de réponse comme mauvais. Notez aussi le chiffre sur une page qui ne peut pas être mise en cache, comme le panier d'une boutique : c'est celle-là que PHP-FPM et Redis vont améliorer. Pour interpréter ces valeurs, voir WordPress lent : trouver la vraie cause.
2. Activer et dimensionner OPcache
OPcache garde en mémoire le code PHP déjà compilé. Il est installé avec PHP, mais ses valeurs par défaut sont trop petites pour WordPress et ses extensions. Créez un fichier de surcharge :
cat > /etc/php/8.3/fpm/conf.d/99-opcache.ini <<'EOF'
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
EOF
systemctl restart php8.3-fpm
Avec revalidate_freq=60, PHP vérifie au plus une fois par minute si un fichier a changé : c'est un bon compromis pour un WordPress mis à jour depuis l'administration.
opcache.validate_timestamps=0. C'est plus rapide, mais PHP ne voit plus aucune modification de fichier tant que vous ne rechargez pas PHP-FPM. À réserver aux applications déployées par un script qui exécute systemctl reload php8.3-fpm à chaque mise en production. Sur WordPress, une mise à jour d'extension ne serait pas prise en compte.3. Dimensionner PHP-FPM
Chaque requête PHP occupe un processus. Quand ils sont tous occupés, les visiteurs suivants attendent. Le réglage clé est pm.max_children, le nombre maximum de processus. Trop bas, le site fait la queue ; trop haut, le serveur manque de mémoire et devient très lent.
Mesurez d'abord la mémoire moyenne d'un processus, site en fonctionnement :
ps --no-headers -o rss -C php-fpm8.3 | awk '{s+=$1; n++} END {printf "%.0f Mo en moyenne\n", s/n/1024}'
Puis calculez : mémoire réservée à PHP ÷ mémoire par processus. Exemple sur un VPS de 4 Go : on garde environ 1,5 Go pour le système, Nginx et MariaDB, il reste 2,5 Go pour PHP. Avec 60 Mo par processus, cela donne une quarantaine ; on retient 35 pour garder de la marge.
Réglez le pool dans /etc/php/8.3/fpm/pool.d/www.conf :
pm = dynamic
pm.max_children = 35
pm.start_servers = 6
pm.min_spare_servers = 4
pm.max_spare_servers = 10
pm.max_requests = 500
pm.max_requests recycle chaque processus après 500 requêtes, ce qui neutralise les petites fuites de mémoire de certaines extensions. Sur un VPS de 2 Go qui héberge peu de trafic, pm = ondemand libère la mémoire quand le site est calme.
Pour savoir si la limite est atteinte, surveillez ce message dans le journal :
grep "max_children" /var/log/php8.3-fpm.log
S'il apparaît régulièrement, augmentez la valeur si la mémoire le permet, sinon passez à une offre supérieure.
4. Mettre en place le cache de pages FastCGI
C'est le réglage qui a le plus d'effet : Nginx garde une copie du HTML généré et la sert directement, sans lancer PHP ni interroger la base. Une page en cache répond en quelques millisecondes.
Déclarez la zone de cache dans /etc/nginx/conf.d/fastcgi-cache.conf :
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=WP:100m inactive=60m max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout updating http_500 http_503;
Puis, dans le bloc server du site, définissez ce qui ne doit jamais être mis en cache :
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") { set $skip_cache 1; }
if ($request_uri ~* "/panier/|/commande/|/mon-compte/|/cart/|/checkout/|/my-account/") { set $skip_cache 1; }
if ($http_cookie ~* "comment_author|wordpress_logged_in|wp-postpass|woocommerce_items_in_cart|woocommerce_cart_hash") { set $skip_cache 1; }
Et dans le bloc location ~ \.php$ :
fastcgi_cache WP;
fastcgi_cache_valid 200 301 302 60m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache $upstream_cache_status;
mkdir -p /var/cache/nginx/fastcgi
nginx -t && systemctl reload nginx
5. Vider le cache après une modification
Nginx ne sait pas qu'un article a été modifié : sans purge, l'ancienne version reste servie jusqu'à expiration, soit 60 minutes ici. Deux solutions :
- Simple : garder une durée courte et vider manuellement après une modification importante avec
rm -rf /var/cache/nginx/fastcgi/*. - Automatique : installer le module de purge (
apt install libnginx-mod-http-cache-purge) et l'extension WordPress Nginx Helper, qui purge la page concernée à chaque publication.
6. Ajouter un cache objet Redis
Le cache de pages ne sert à rien sur les pages dynamiques : panier, compte client, administration. Pour celles-là, un cache objet évite de répéter les mêmes requêtes SQL d'une page à l'autre.
apt install -y redis-server php8.3-redis
systemctl enable --now redis-server
systemctl restart php8.3-fpm
Installez ensuite l'extension WordPress Redis Object Cache et activez-la dans Réglages → Redis. Par défaut, Redis n'écoute que sur l'adresse locale : ne l'exposez jamais sur Internet.
7. Activer la compression
La compression gzip est déjà active dans la configuration Nginx d'Ubuntu, mais seulement pour le HTML. Complétez la section http de /etc/nginx/nginx.conf :
gzip on;
gzip_vary on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types text/css text/plain text/xml application/javascript application/json
application/xml application/rss+xml image/svg+xml font/ttf;
Inutile d'ajouter les images JPEG, PNG ou WebP : elles sont déjà compressées.
8. Vérifier le résultat
Appelez deux fois la même page et regardez l'en-tête ajouté plus haut :
curl -sI https://monsite.fr/ | grep -i x-cache
curl -sI https://monsite.fr/ | grep -i x-cache
La première réponse indique MISS, la seconde HIT. Relancez ensuite la mesure de l'étape 1 : sur une page en cache, le temps de réponse doit descendre sous les 100 ms depuis la France. Sur les pages dynamiques, c'est OPcache, PHP-FPM et Redis qui font la différence.