« WordPress est lent » n’est pas un diagnostic. C’est une plainte, comme « la voiture fait un drôle de bruit ». Parfois c’est un plugin qui exécute 400 requêtes SQL par page. Parfois c’est le serveur qui s’essouffle parce que l’I/O disque est saturé. Parfois votre cache fonctionne parfaitement… pour les utilisateurs anonymes, tandis que le trafic connecté rampe comme s’il payait à la requête.
Ce guide vous aide à arrêter les suppositions. Nous parcourrons la pile de l’extérieur vers l’intérieur — réseau, serveur web, PHP-FPM, base de données, stockage, plugins — en utilisant des commandes que vous pouvez lancer aujourd’hui, et des décisions à prendre selon ce que vous observez.
Procédure rapide de diagnostic
Si vous n’avez que 20 minutes avant que quelqu’un commence à « essayer des plugins au hasard », faites ceci. L’objectif est de répondre à une question : où passe le temps ? Le second objectif est de le prouver avec des éléments concrets, pas des impressions.
1) D’abord : mesurer le TTFB et séparer hits et misses de cache
- Vérifiez une page publique non authentifiée. Puis une page en session (connectée). Comparez le TTFB et le temps total.
- Si les pages publiques sont rapides et que l’administration est lente, ne touchez pas encore aux configs Nginx. C’est généralement le PHP, la base de données ou le comportement d’un plugin qui contournent les caches.
2) Ensuite : vérifier la saturation sur l’hôte (CPU, pression mémoire, I/O disque)
- CPU élevé avec iowait faible : trop de travail PHP ou trop de requêtes concurrentes.
- iowait élevé : goulot de stockage ou base de données générant trop d’I/O aléatoire.
- Activité de swap : vous êtes déjà en retard ; corrigez la pression mémoire d’abord.
3) Troisième étape : vérifier le queueing PHP-FPM et les slow logs
- Si FPM atteint le nombre maximal de workers ou met des requêtes en file d’attente, vous verrez un TTFB lent et « ça empire sous charge ».
- Activez temporairement le slowlog PHP-FPM. C’est la confession la plus fiable.
4) Quatrième étape : vérifier MySQL pour les requêtes lentes et la contention
- Slow query log + processlist + InnoDB status vous diront si la base de données est le frein.
- Si vous voyez beaucoup de « Sending data » ou des requêtes longues sur wp_options, vous êtes dans le monde WordPress, pas dans celui du noyau.
5) Cinquième étape : identifier quel plugin/thème/chemin de code est coûteux
- Utilisez WP-CLI pour désactiver rapidement des plugins suspects en staging ou pendant une fenêtre contrôlée.
- Corrélez : chemin de requête → trace PHP (slowlog) → empreintes de requêtes (slow query log).
C’est tout. Cinq étapes. Si vous les suivez, vous arrêterez de débattre « Apache vs Nginx » et commencerez à réparer le vrai goulot.
Quelques faits et historique (utile, pas pour un quiz)
- WordPress a commencé en 2003 comme un fork de b2/cafelog, conçu pour un web plus lent où le rendu côté serveur et le cache de pages étaient la norme.
- Le cache de requêtes MySQL était autrefois courant pour WordPress, mais il a été déprécié et supprimé dans MySQL moderne car il créait de la contention en concurrence.
- WP-Cron n’est pas un vrai cron ; c’est un mécanisme « exécuter les tâches planifiées lors des visites ». Sous faible trafic il est peu fiable ; sous fort trafic il peut être bruyant.
- WooCommerce a changé la culture performance WordPress : il a transformé « un blog » en « un système transactionnel », où la latence coûte de l’argent et les travaux en arrière-plan importent.
- PHP 7 a fait une différence majeure dans les performances WordPress comparé à PHP 5.x. Beaucoup d’histoires « WordPress est lent » d’antan étaient en fait « PHP était lent ».
- Les options autoloadées dans wp_options sont chargées à chaque requête. Un autoload gonflé peut pénaliser chaque page, mise en cache ou non.
- HTTP/2 a réduit la douleur des petits assets, mais n’a rien fait pour un TTFB backend lent. Les gens confondent encore « chargement lent » et « téléchargement lent ».
- Le caching objet (Redis/Memcached) aide lorsque les mêmes requêtes coûteuses se répètent, mais il peut aussi masquer un problème de requête sous-jacent jusqu’à ce que le cache soit évincé.
Définir « lent » en chiffres, pas en impressions
Vous ne pouvez pas optimiser ce que vous ne décrivez pas. « Lent » a besoin d’au moins trois nombres :
- TTFB (Time To First Byte) : temps backend plus latence réseau jusqu’au premier octet de réponse. C’est là que se manifestent PHP, DB et les attentes en amont.
- Temps de chargement total : inclut images, JS, CSS. Peut être « backend rapide, front lourd ». Problème différent.
- Percentiles sous charge : p50 correspond à une journée normale ; p95 là où vos clients commencent à partir ; p99 là où votre canal d’incident se réveille.
Décidez aussi : déboguons-nous le trafic public anonyme ou le trafic connecté/admin ? Le caching WordPress favorise généralement les pages anonymes. Les chemins connectés contournent souvent les caches et touchent toute la pile à chaque fois.
Commencer par l’extérieur : isoler le TTFB, réseau, CDN et cache
Avant d’ouvrir une session SSH, prouvez si le problème vient du backend ou de la livraison frontend. Le piège le plus fréquent est d’optimiser la base de données quand le CDN est mal configuré et que chaque requête est un cache miss. Le second piège est d’optimiser le CDN alors que le backend passe 4 secondes à générer le HTML.
Vérification de la réalité du cache
Si vous utilisez un CDN ou un reverse proxy (Cloudflare, Fastly, Varnish, cache Nginx), la première question est : ai-je des cache hits ? Cherchez les en-têtes de cache. S’il n’y en a pas, vous naviguez à l’aveugle. Ajoutez-les ou configurez-les. Un cache qu’on ne peut pas observer n’est qu’une rumeur.
Différencier « premier octet lent » et « téléchargement lent »
Un TTFB élevé signifie backend ou attente en amont. Un téléchargement lent signifie taille du payload ou goulot côté client. Si votre TTFB est de 3 secondes et la page de 200 KB, ce n’est pas le Wi‑Fi de l’utilisateur. C’est vous.
Blague n°1 : Si votre TTFB se mesure en secondes, félicitations — vous avez construit un très petit job batch et l’avez accidentellement exposé comme site web.
Goulots côté serveur : CPU, mémoire, I/O disque et saturation
Quand WordPress ralentit, le serveur vous dit presque toujours pourquoi. Vous devez juste poser les bonnes questions dans le bon ordre. Règle SRE : mesurez la saturation en premier, car la saturation crée des symptômes « aléatoires » partout ailleurs.
CPU : quand PHP devient un radiateur
Un CPU élevé avec un iowait faible signifie généralement que PHP est actif : plugins lourds, templates coûteux, trop de requêtes concurrentes ou un effet de masse dû aux cache misses.
Mais prenez garde : « CPU à 100 % » sur une petite VM peut simplement signifier que vous faites tourner une boutique de production sur une machine taillée pour un blog personnel. J’admire l’optimisme.
Pression mémoire : le tueur de performance bien maquillé
Peu de mémoire libre n’est pas forcément mauvais ; Linux utilise la mémoire pour le cache. Le mauvais signe est l’activité de swap ou une augmentation des major page faults. Si la machine swappe, votre latence devient volatile. Si elle swappe sous charge, votre site commence à « timeouter » de manière aléatoire.
I/O disque : l’iowait est l’odeur, pas le feu
Un iowait élevé signifie que le CPU attend le stockage. WordPress peut déclencher des problèmes I/O via :
- MySQL effectuant des lectures aléatoires parce que des index manquent ou que le buffer pool est trop petit.
- Nombreuses petites lectures de fichiers PHP sur un stockage réseau lent.
- Logging à des taux absurdes (access logs, debug logs, slow logs sur des sites très actifs sans rotation).
- Backups ou anti-virus saturant les disques.
Cache du noyau et du système de fichiers : rapide jusqu’à ce que ce ne le soit plus
Le page cache Linux rend les lectures répétées rapides — jusqu’à ce que la pression mémoire force l’éviction. Si les performances s’effondrent après un déploiement ou une vidange de cache, puis « se réchauffent » lentement, cela peut venir du cache système de fichiers ou du comportement du cache applicatif/objet. Différentes corrections, symptômes similaires.
Serveur web et PHP-FPM : les suspects habituels
Dans une pile WordPress moderne, la requête circule généralement : Nginx/Apache → PHP-FPM → WordPress → MySQL → retour. Votre goulot apparaît souvent comme un enchaînement de files d’attente quelque part dans cette chaîne.
Serveur web : restez basique
Nginx et Apache peuvent tous les deux très bien servir WordPress. La plupart des problèmes de « performance du serveur web » ne tiennent pas au choix du serveur ; ils viennent d’une mauvaise configuration, d’un manque de cache ou d’un surcoût TLS sur du matériel sous-dimensionné.
PHP-FPM : là où le queueing devient visible pour l’utilisateur
PHP-FPM a un mode d’échec simple : trop de requêtes concurrentes, pas assez de workers, et chaque worker reste occupé trop longtemps. Ça crée une file. La file crée de la latence. Ensuite le load balancer réessaie. Ensuite vous avez plus de trafic. Ensuite tout le monde apprend de nouveaux mots.
Le slow log de PHP-FPM est votre ami. Il pointe le chemin de code. Il met aussi fin aux débats. Si le slow log indique que 70 % du temps est passé dans l’appel API d’un plugin, vous n’avez pas besoin de débat philosophique sur les réglages d’opcache.
OPcache : le gain à faible risque
OPcache n’est pas optionnel en production WordPress. Sans lui, vous recompilerez constamment les scripts PHP. Ce n’est pas « dynamique ». C’est du gaspillage. Assurez-vous qu’il est activé, dimensionné raisonnablement et ne redémarre pas sans cesse.
Base de données : goulots MySQL/MariaDB et pathologies de requêtes
WordPress utilise MySQL comme un cheval de trait, et parfois comme un mulet loué. La base de données est souvent le goulot parce qu’elle est partagée, sous-dimensionnée, ou sollicitée par des requêtes pathologiques.
Points classiques de douleur dans la base WordPress
- Gonflement de l’autoload dans wp_options : des options autoloadées énormes signifient que chaque requête traîne une valise de données de la base vers la mémoire PHP.
- Requêtes meta non indexées : wp_postmeta et wp_usermeta peuvent devenir des cimetières de requêtes.
- Requêtes WooCommerce sur les commandes : jointures complexes et recherches meta peuvent écraser un stockage lent.
- Verrouillage : transactions longues ou ALTER bloquant peuvent empêcher les lectures/écritures et provoquer des pics.
Slow query log : le meilleur document « pourquoi c’est lent » que vous puissiez produire
Activez le slow query log avec un seuil bas temporairement (par exemple 0.5–1s) pour capturer les pires coupables. Groupez ensuite par empreinte (forme de requête), pas par texte exact. La même requête répétée est le vrai centre de coût.
InnoDB buffer pool : le levier mémoire
Si le buffer pool est minuscule par rapport au working set, MySQL devient un générateur d’I/O. Sur des hôtes DB dédiés, le buffer pool est souvent réglé à ~60–75 % de la RAM. Sur des hôtes partagés, il faut être prudent sinon MySQL combat l’OS et perd.
Citation (idée paraphrasée) : « L’espoir n’est pas une stratégie. » — souvent citée en culture opérations, liée à la pensée sur la fiabilité.
Couche WordPress et plugins : prouver qui est coupable
Les plugins sont du code que vous n’avez pas écrit, s’exécutant avec les mêmes privilèges que votre code. Traitez-les comme des fournisseurs avec un accès shell : supposez qu’ils feront quelque chose de surprenant, un jour.
Patterns coûteux que vous pouvez réellement détecter
- Trop de requêtes par requête : surtout dans les écrans d’administration et WooCommerce.
- Appels HTTP externes lors du chargement : pixels marketing, vérifications de licence, appels API qui « phone home ».
- Logique de template non mise en cache : boucles qui déclenchent des requêtes N+1.
- Junk autoloadé : stocker de grands tableaux dans des options avec autoload=yes.
- Recherche et filtrage : recherche mal indexée, LIKE avec wildcard, requêtes meta sans contraintes.
Ne « optimisez » pas en ajoutant plus de plugins
Les plugins de cache peuvent aider, mais les empiler est un hobby, pas un plan d’ingénierie. Un cache de page, un cache objet, une stratégie CDN. Choisissez délibérément. Observez. Itérez.
Blague n°2 : À chaque fois que vous installez « Ultimate Speed Booster Pro », une requête lente prend son envol.
WP-Cron et travaux en arrière-plan : le coût caché
WP-Cron exécute les tâches planifiées lorsqu’un visiteur charge le site. Cela signifie :
- Sur les sites à faible trafic, les tâches peuvent ne pas s’exécuter à l’heure.
- Sur les sites à fort trafic, les tâches peuvent s’exécuter trop souvent, se chevaucher et provoquer des pics CPU/DB.
Si votre site ralentit « toutes les quelques minutes », WP-Cron est un suspect principal. Il en va de même pour les backups, imports, cache warmers, tâches d’optimisation d’images et synchronisations de newsletters.
La solution mature est de désactiver le déclenchement WP-Cron sur les pages et de l’exécuter via cron système à un rythme contrôlé.
Tâches pratiques (commandes + ce que la sortie signifie + la décision à prendre)
Ce sont des vérifications de type production. Exécutez-les dans l’ordre. Chacune vous indique quoi faire ensuite. C’est le but.
Tâche 1 : Mesurer le TTFB et le temps total depuis le bord du serveur
cr0x@server:~$ curl -s -o /dev/null -w "namelookup:%{time_namelookup}\nconnect:%{time_connect}\nstarttransfer:%{time_starttransfer}\ntotal:%{time_total}\n" https://example.com/
namelookup:0.004
connect:0.021
starttransfer:1.842
total:1.913
Signification : starttransfer est essentiellement le TTFB (plus réseau). Ici il est ~1.84s, donc le backend est lent.
Décision : Si starttransfer est élevé mais connect faible, arrêtez de blâmer DNS/CDN. Passez au serveur/PHP/DB.
Tâche 2 : Comparer réponse anonyme vs connecté/admin
cr0x@server:~$ curl -s -o /dev/null -w "TTFB:%{time_starttransfer} total:%{time_total}\n" https://example.com/wp-admin/
TTFB:3.912 total:4.105
Signification : wp-admin est beaucoup plus lent. Les caches sont probablement contournés ; le PHP/DB travaille davantage.
Décision : Priorisez l’inspection de PHP-FPM et de la DB ; ne perdez pas de temps à tuner le cache des assets statiques.
Tâche 3 : Vérifier rapidement la charge système, CPU et iowait
cr0x@server:~$ uptime
14:22:18 up 36 days, 2:03, 1 user, load average: 8.72, 7.91, 6.88
cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.5.0 (server) 12/27/2025 _x86_64_ (4 CPU)
Average: CPU %usr %nice %sys %iowait %irq %soft %steal %idle
Average: all 62.11 0.00 7.42 18.93 0.00 0.37 0.00 11.17
Signification : la charge moyenne est élevée par rapport à 4 CPU, et l’iowait est ~19 %. Le disque contribue.
Décision : Vérifiez la latence disque et les schémas I/O MySQL ensuite ; n’augmentez pas simplement les workers PHP.
Tâche 4 : Voir si la pression mémoire ou le swap interviennent
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 8.0Gi 6.7Gi 220Mi 210Mi 1.1Gi 680Mi
Swap: 2.0Gi 1.2Gi 820Mi
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
3 1 1249280 225384 9120 988224 10 24 612 430 510 890 62 7 12 19 0
Signification : le swap est utilisé et il y a des entrées/sorties de swap en cours (si/so). Cela provoque des pics de latence.
Décision : Réduisez la pression mémoire : ajustez la taille de la VM, réduisez PHP-FPM max children, corrigez la mémoire MySQL, ou ajoutez de la RAM avant les micro-optimisations.
Tâche 5 : Identifier les plus gros consommateurs CPU et mémoire
cr0x@server:~$ ps -eo pid,comm,%cpu,%mem --sort=-%cpu | head
2841 php-fpm8.2 38.2 3.1
2910 php-fpm8.2 34.9 3.0
1732 mysqld 21.4 24.8
1120 nginx 3.1 0.4
Signification : PHP-FPM grignote le CPU, MySQL est lourd en mémoire (ce qui peut être normal) et consomme aussi du CPU.
Décision : Inspectez la saturation des workers PHP-FPM et le comportement des requêtes MySQL en parallèle.
Tâche 6 : Vérifier la latence disque et la profondeur de file
cr0x@server:~$ iostat -x 1 3
Linux 6.5.0 (server) 12/27/2025 _x86_64_ (4 CPU)
Device r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await %util
nvme0n1 52.0 38.0 3.1 5.7 175.2 4.21 28.40 21.10 37.20 94.8
Signification : await ~28ms et %util ~95 % signifient que le disque est saturé. MySQL et les lectures de fichiers PHP vont en souffrir.
Décision : Réduisez l’I/O : corrigez les requêtes lentes/indexs, augmentez le buffer pool MySQL si la mémoire le permet, migrez la DB vers un stockage plus rapide, ou séparez l’hôte DB.
Tâche 7 : Vérifier l’état du pool PHP-FPM pour le queueing (si activé)
cr0x@server:~$ sudo curl -s http://127.0.0.1/status?full | sed -n '1,25p'
pool: www
process manager: dynamic
start time: 27/Dec/2025:13:55:21 +0000
start since: 1602
accepted conn: 19482
listen queue: 37
max listen queue: 211
listen queue len: 128
idle processes: 0
active processes: 24
total processes: 24
max active processes: 24
max children reached: 19
slow requests: 83
Signification : la file d’écoute n’est pas nulle et « max children reached » est élevé. Des requêtes attendent des workers.
Décision : N’augmentez pas aveuglément max children. Réduisez d’abord le coût par requête (slowlog, DB) et confirmez la marge mémoire ; sinon vous amplifierez le swap et aggraverez la situation.
Tâche 8 : Activer et lire le slowlog PHP-FPM (temporaire, ciblé)
cr0x@server:~$ sudo grep -nE 'slowlog|request_slowlog_timeout' /etc/php/8.2/fpm/pool.d/www.conf
261:request_slowlog_timeout = 2s
262:slowlog = /var/log/php8.2-fpm.slow.log
cr0x@server:~$ sudo tail -n 20 /var/log/php8.2-fpm.slow.log
[27-Dec-2025 14:20:11] [pool www] pid 2910
script_filename = /var/www/html/index.php
[0x00007f2a8c1a3f60] wp_remote_get() /var/www/html/wp-includes/http.php:271
[0x00007f2a8c1a3df0] some_plugin_license_check() /var/www/html/wp-content/plugins/some-plugin/core.php:812
Signification : PHP est bloqué sur un appel HTTP sortant à l’intérieur d’un plugin.
Décision : Corrigez le comportement du plugin (désactiver, configurer, mettre en cache les résultats, déplacer en asynchrone). Augmenter le CPU ne résoudra pas une attente réseau.
Tâche 9 : Confirmer qu’OPcache est activé et correctement dimensionné
cr0x@server:~$ php -i | grep -E 'opcache.enable|opcache.memory_consumption|opcache.max_accelerated_files' | head -n 5
opcache.enable => On => On
opcache.memory_consumption => 256 => 256
opcache.max_accelerated_files => 20000 => 20000
Signification : OPcache est activé et dimensionné raisonnablement pour de nombreux fichiers WP.
Décision : Si OPcache est désactivé, activez-le. Si la mémoire est trop faible, vous verrez des réinitialisations fréquentes et un surcoût CPU.
Tâche 10 : Vérifier les requêtes MySQL en cours et les symptômes de verrouillage
cr0x@server:~$ sudo mysql -e "SHOW FULL PROCESSLIST\G" | sed -n '1,60p'
*************************** 1. row ***************************
Id: 2198
User: wp
Host: 127.0.0.1:49822
db: wordpress
Command: Query
Time: 12
State: Sending data
Info: SELECT option_name, option_value FROM wp_options WHERE autoload = 'yes'
*************************** 2. row ***************************
Id: 2202
User: wp
Host: 127.0.0.1:49836
db: wordpress
Command: Query
Time: 9
State: statistics
Info: SELECT * FROM wp_postmeta WHERE meta_key = '_price' AND meta_value BETWEEN '10' AND '50'
Signification : Requêtes longues. La requête autoload devrait être rapide ; si ce n’est pas le cas, wp_options est gonflé ou le serveur est I/O bound. La requête postmeta est un classique point de douleur.
Décision : Activez le slow query log et inspectez les index/la taille de l’autoload. Envisagez des stratégies d’indexation spécifiques à WooCommerce et réduisez les requêtes meta.
Tâche 11 : Activer le slow query log (fenêtre courte) et l’inspecter
cr0x@server:~$ sudo mysql -e "SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 0.5; SET GLOBAL log_output = 'FILE';"
cr0x@server:~$ sudo tail -n 20 /var/log/mysql/mysql-slow.log
# Time: 2025-12-27T14:24:02.118532Z
# User@Host: wp[wp] @ localhost [] Id: 2251
# Query_time: 1.734 Lock_time: 0.000 Rows_sent: 1 Rows_examined: 874221
SET timestamp=1766845442;
SELECT * FROM wp_postmeta WHERE meta_key = '_price' AND meta_value BETWEEN '10' AND '50';
Signification : Rows_examined est énorme par rapport aux lignes retournées. C’est un problème d’index manquant / schéma inadapté.
Décision : Arrêtez de tuner les tailles de buffer en premier. Corrigez le chemin de requête : ajustez le comportement du plugin, réduisez le filtrage meta, ajoutez des index appropriés si sûr, ou utilisez des tables de recherche dédiées quand disponible.
Tâche 12 : Vérifier l’efficacité du InnoDB buffer pool
cr0x@server:~$ sudo mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
+---------------------------------------+-----------+
| Variable_name | Value |
+---------------------------------------+-----------+
| Innodb_buffer_pool_read_requests | 987654321 |
| Innodb_buffer_pool_reads | 3456789 |
+---------------------------------------+-----------+
Signification : Buffer pool reads indique des lectures depuis le disque ; requests sont des lectures logiques. Si reads est élevé par rapport à requests, votre working set ne tient pas en mémoire.
Décision : Si vous avez de la marge en RAM, augmentez innodb_buffer_pool_size. Sinon, réduisez le working set (nettoyez les autoloads, corrigez les requêtes, archivez les anciennes données).
Tâche 13 : Trouver la taille des options autoloadées (tueur silencieux classique)
cr0x@server:~$ sudo mysql -D wordpress -e "SELECT ROUND(SUM(LENGTH(option_value))/1024/1024,2) AS autoload_mb FROM wp_options WHERE autoload='yes';"
+------------+
| autoload_mb|
+------------+
| 18.47 |
+------------+
Signification : 18 Mo d’options autoloadées est énorme ; cela se charge fréquemment en mémoire et peut ralentir chaque requête non cachée.
Décision : Identifiez les plus grosses options autoloadées et corrigez la source (paramètres de plugin, mauvaise utilisation des transients). Visez quelques Mo au maximum.
Tâche 14 : Identifier les plus grosses options autoloadées (pour savoir qui incriminer)
cr0x@server:~$ sudo mysql -D wordpress -e "SELECT option_name, autoload, ROUND(LENGTH(option_value)/1024,1) AS kb FROM wp_options WHERE autoload='yes' ORDER BY LENGTH(option_value) DESC LIMIT 10;"
+-------------------------------+----------+------+
| option_name | autoload | kb |
+-------------------------------+----------+------+
| some_plugin_big_settings | yes | 5120 |
| rewrite_rules | yes | 980 |
| another_plugin_cache_blob | yes | 740 |
+-------------------------------+----------+------+
Signification : Un plugin autoload de gros blobs multi-Mo. Ce ne sont pas des « paramètres », c’est un appel à l’aide.
Décision : Reconfigurez/remplacez le plugin, ou passez autoload à no pour des options spécifiques (prudemment, après validation). Purgez et reconstruisez aussi les rewrite rules si cette entrée est gonflée.
Tâche 15 : Vérifier si WP-Cron surcharge les requêtes
cr0x@server:~$ wp cron event list --fields=hook,next_run,recurrence --format=table
+------------------------------+---------------------+------------+
| hook | next_run | recurrence |
+------------------------------+---------------------+------------+
| wp_version_check | 2025-12-27 14:25:00 | twice_daily|
| some_plugin_sync_job | 2025-12-27 14:23:00 | every_min |
| woocommerce_cleanup_sessions | 2025-12-27 14:26:00 | hourly |
+------------------------------+---------------------+------------+
Signification : Les jobs « every_min » peuvent être légitimes, mais souvent ils se chevauchent et causent des pics périodiques.
Décision : Déplacez cron vers cron système, réduisez la fréquence et assurez-vous que les jobs longs ont des verrous pour éviter les chevauchements.
Tâche 16 : Tester rapidement l’impact d’un plugin (staging ou fenêtre contrôlée)
cr0x@server:~$ wp plugin list --status=active --format=table
+---------------------+--------+-----------+---------+
| name | status | update | version |
+---------------------+--------+-----------+---------+
| woocommerce | active | available | 8.4.0 |
| some-plugin | active | none | 3.2.1 |
| seo-suite | active | none | 19.0 |
+---------------------+--------+-----------+---------+
cr0x@server:~$ wp plugin deactivate some-plugin
Plugin 'some-plugin' deactivated.
Signification : Vous pouvez valider si le coupable du slowlog provoque vraiment la latence.
Décision : Si la latence baisse sensiblement, gardez-le désactivé et planifiez un remplacement ou une correction fournisseur. Si rien ne change, réactivez et poursuivez la recherche.
Trois mini-histoires d’entreprise (comment ça tourne mal en vrai)
Mini-histoire 1 : L’incident causé par une mauvaise hypothèse
Une entreprise opérait un site WordPress riche en contenu avec une boutique WooCommerce séparée. Le site de contenu a commencé à rendre des timeouts lors d’une campagne. Tout le monde a supposé que le CDN était en cause parce que « nous avons changé les règles de cache la semaine dernière ». Cela semblait plausible. C’était aussi faux.
L’ingénieur d’astreinte a fait la chose ennuyeuse : curl timing depuis plusieurs régions, puis SSH, puis iostat. Le TTFB était élevé même depuis l’intérieur du VPC. Le %util du disque était bloqué, iowait élevé. MySQL était local sur la même VM. Le CDN était innocent ; il livrait fidèlement des réponses lentes.
La mauvaise hypothèse était que « les pages statiques devraient être en cache, donc l’origine ne peut pas être le problème ». En réalité, des éditeurs connectés frappaient des endpoints non mis en cache et déclenchaient de lourds chargements d’autoload à chaque requête. La campagne avait augmenté l’activité d’admin : plus de brouillons, de révisions, d’aperçus et d’appels API pilotés par des plugins.
La correction n’était pas héroïque : réduire le gonflement des autoloads, séparer la DB sur une instance plus rapide, et ajouter de l’observabilité sur le taux de hits du cache et la profondeur de file PHP-FPM. L’incident s’est terminé quand ils ont arrêté de débattre du bord et ont commencé à mesurer l’origine.
Mini-histoire 2 : L’optimisation qui a mal tourné
Une autre équipe avait des pages de checkout lentes et a décidé de « résoudre ça par le cache ». Ils ont ajouté une couche de cache de page et poussé les TTL à fond. Les pages produit anonymes avaient l’air parfaites. Les graphiques du tableau de bord se sont améliorés. On a fêté ça.
Puis les tickets support ont explosé. Les acheteurs voyaient des stocks périmés et des prix incohérents. Les utilisateurs connectés étaient toujours lents, et maintenant le système était plus difficile à raisonner parce que les caches masquaient des problèmes backend. Le cache de page mettait aussi en cache incorrectement des fragments personnalisés. Rien de tel que livrer une amélioration de performance qui apporte aussi de la confusion.
Quand ils ont enfin activé le slowlog PHP-FPM, le coupable était un plugin appelant des API externes de taxe/livraison synchrones lors du checkout, sans gestion de timeout et sans cache des résultats. L’« optimisation » n’avait pas touché ce chemin ; elle avait juste rendu quelques graphiques plus verts.
Ils ont freiné le caching agressif, mis en place des timeouts raisonnables, ajouté des fallback asynchrones quand les règles métier le permettaient, et mis en cache les réponses API avec des clés prudentes. La performance s’est améliorée, la correction est revenue, et tout le monde a tiré la même leçon : le cache ne remplace pas la compréhension.
Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la situation
Une organisation média faisait tourner WordPress à haut trafic stable. Rien de glamour : cycles d’actualité, beaucoup de lectures, pics occasionnels. Leur arme secrète n’était pas un plugin magique. C’était la discipline.
Ils avaient le slow query logging activé avec un seuil raisonnable, faisaient tourner et expédiaient les logs, et passaient en revue les empreintes de requêtes principales chaque semaine. Pas pendant les incidents. Chaque semaine. Ils suivaient aussi systématiquement « max children reached » de PHP-FPM et le hit rate du buffer pool MySQL comme métriques de santé.
Un vendredi après‑midi, la latence a commencé à augmenter doucement. Pas encore de panne — juste une hausse subtile du p95. Comme ils avaient des baselines, l’astreinte a immédiatement vu les lectures du buffer pool augmenter et la latence disque grimper. Cela corrélait avec un nouveau déploiement de fonctionnalité dans un plugin qui introduisait une requête meta sur des données de haute cardinalité.
Ils ont rollbacké le changement du plugin, ajouté un index en staging, vérifié le plan de requête, puis redéployé. Les utilisateurs n’ont presque rien remarqué. L’équipe est rentrée chez elle. L’observabilité ne les a pas rendus plus rapides à taper ; elle les a rendus moins surpris.
Erreurs fréquentes : symptôme → cause → correction
1) Symptom : la page d’accueil est rapide, wp-admin est terriblement lent
Cause : le cache de page aide les utilisateurs anonymes ; l’administration contourne le cache et déclenche des requêtes DB lourdes, des appels externes ou un autoload gonflé.
Correction : activez le slowlog PHP-FPM ; vérifiez la taille de l’autoload ; auditez les plugins qui s’accrochent aux écrans admin ; ajoutez un cache objet ; réduisez l’autoload de wp_options.
2) Symptom : la performance est correcte jusqu’à un pic de trafic, puis tout timeout
Cause : saturation et queueing : PHP-FPM max children atteint, limites de connexions DB, ou I/O disque saturé.
Correction : mesurez la profondeur de file (FPM status), augmentez la capacité prudemment (plus de CPU/RAM/disque plus rapide), ajoutez du caching et réduisez le travail par requête.
3) Symptom : lenteurs périodiques toutes les quelques minutes
Cause : WP-Cron ou jobs planifiés qui se chevauchent, backups, erreurs de logrotate, ou tâches de synchronisation externes.
Correction : déplacez WP-Cron vers cron système ; ajoutez des verrous ; planifiez les jobs lourds hors‑pic ; surveillez la durée des crons.
4) Symptom : CPU bas, mais les pages prennent toujours des secondes avant de commencer à charger
Cause : I/O bloquante : disque lent, DNS sortant lent, API externes, ou attentes DB.
Correction : vérifiez iowait/iostat ; inspectez le slowlog PHP pour wp_remote_* ; ajoutez des timeouts et du caching pour les appels externes.
5) Symptom : après l’installation d’un plugin, le TTFB double
Cause : le plugin ajoute des hooks coûteux, des options autoloadées, ou des requêtes meta lourdes sur chaque requête.
Correction : désactivez et confirmez ; inspectez slowlog et slow query log ; remplacez le plugin ou changez sa configuration ; nettoyez l’autoload.
6) Symptom : CPU DB élevé, beaucoup de « Sending data » dans les requêtes
Cause : requêtes non indexées, scans de tables massifs, ou Rows_examined élevé dans le slow log. Parfois causé par des fonctionnalités de recherche/filtre.
Correction : analysez les empreintes des requêtes lentes ; ajoutez/ajustez les index prudemment ; réduisez l’usage des requêtes meta ; envisagez un modèle de données alternatif pour les attributs produit.
7) Symptom : rapide après un redémarrage, lent ensuite
Cause : fuites mémoire ou croissance de cache (cache objet, fragmentation OPcache), ou éviction du buffer pool due à une croissance du working set.
Correction : surveillez la mémoire dans le temps ; confirmez les réglages OPcache ; assurez-vous que le buffer pool MySQL est dimensionné correctement ; plafonnez les caches et purgez-les intelligemment.
Listes de contrôle / plan étape par étape
Checklist A : triage de 30 minutes (sûr en production)
- Mesurez TTFB et temps total pour une URL publique et une URL admin.
- Vérifiez la saturation hôte : uptime, mpstat, vmstat, iostat.
- Inspectez le statut PHP-FPM : longueur de file, max children reached, slow requests.
- Vérifiez le processlist MySQL pour les requêtes longues ; capturez l’état InnoDB si besoin.
- Si le disque est saturé : priorisez corrections de requêtes DB et upgrades de stockage plutôt que « plus de workers PHP ».
- Si le CPU est saturé côté PHP : activez le slowlog et identifiez le chemin de code.
- Notez : ce qui a changé récemment (mise à jour de plugin, changement de thème, croissance DB, basculement de trafic).
Checklist B : plan de stabilisation 1–2 jours
- Activez le slow query log (seuil 0.5–1s) pendant les fenêtres de pic ; collectez les empreintes de requêtes principales.
- Activez le slowlog PHP-FPM à 2s temporairement ; identifiez les piles lentes.
- Auditez la taille de l’autoload wp_options ; réduisez-la de manière agressive mais sûre.
- Déplacez WP-Cron vers cron système ; empêchez les jobs qui se chevauchent.
- Confirmez le dimensionnement OPcache et les compteurs de workers PHP-FPM en fonction de la RAM réelle.
- Ajoutez un cache objet (Redis) si vous avez des patterns de requêtes répétées et des taux de hit stables.
- Testez les changements en staging avec un volume de données semblable à la production. « Ça marche sur une install fraîche » n’est pas un test de performance.
Checklist C : améliorations structurelles (ce qui met fin aux incidents récurrents)
- Séparez la DB du web si la contention des ressources est réelle et constante.
- Mettez la DB sur un stockage rapide et basse latence ; évitez les disques réseau lents pour des stores avec beaucoup d’écritures.
- Instrumentez le taux hit/miss du cache, la file PHP-FPM et la latence DB comme métriques de première importance.
- Établissez une politique plugins : responsables, cadence de mise à jour, plan de rollback et budget de performance.
- Purgez régulièrement révisions, transients, sessions et meta orphelins selon les besoins métier.
- Chargez et testez les grosses releases avec une concurrence réaliste et des flux connectés, pas seulement des hits anonymes sur la page d’accueil.
FAQ
1) Comment savoir si le goulot est le serveur ou la base de données ?
Regardez iowait, await disque et les requêtes lentes MySQL. Si iostat montre un await/%util élevé et que le slow log MySQL montre un Rows_examined élevé, la combinaison DB+disque est probablement le frein.
2) Mon CPU est bas, mais WordPress est quand même lent. Pourquoi ?
Attendre ne consomme pas de CPU. Les attentes disque, réseau (APIs externes) et verrouillages peuvent produire un TTFB élevé avec peu de CPU. Le slowlog PHP-FPM révèle souvent les appels bloquants.
3) Dois‑je augmenter PHP-FPM max_children pour corriger la lenteur ?
Seulement si vous avez de la marge mémoire et que vos requêtes sont déjà efficaces. Si vous swappez ou êtes limité par le disque, plus de workers augmentera la contention et aggravera la latence.
4) Redis rend‑il automatiquement WordPress plus rapide ?
Non. Redis aide quand des recherches coûteuses répétées peuvent être mises en cache en toute sécurité. Il ne corrigera pas des requêtes non indexées qui sont uniques à chaque fois, ni des plugins faisant des appels externes.
5) Pourquoi wp-admin est plus lent que le frontend ?
Les écrans d’administration contournent typiquement les caches de page, exécutent plus de requêtes, chargent plus de code et déclenchent des hooks de plugins. De plus, les cookies de connexion empêchent souvent le CDN de mettre en cache par design.
6) Quelle est la façon la plus rapide d’identifier un mauvais plugin ?
Slowlog PHP-FPM pour obtenir des traces de pile, plus WP-CLI pour désactiver sélectivement des plugins (de préférence en staging). Corrélez avec les requêtes lentes DB. Ne vous fiez pas seulement à « ça semblait commencer après… » sans preuve.
7) WooCommerce est‑il intrinsèquement lent ?
Pas intrinsèquement, mais il est facile de le rendre lent car il stocke beaucoup de données structurées dans des tables meta et déclenche des requêtes complexes. Les parcours de checkout sont aussi sensibles aux intégrations externes.
8) Pourquoi ça devient lent périodiquement, comme une horloge ?
WP-Cron, backups, logrotate, jobs d’indexation, synchronisations produit, ou purges de cache. Corrélez avec les logs cron et l’activité système (pics iostat, pics MySQL) à ces moments.
9) Si je passe à un serveur plus gros, est‑ce que tout sera corrigé ?
Ça peut gagner du temps, et parfois c’est la bonne décision. Mais si vous avez une requête pathologique qui scanne des millions de lignes, elle reviendra quand les données grossiront — souvent au pire moment.
10) Dois‑je utiliser Nginx ou Apache pour la performance ?
Choisissez celui que vous savez opérer. La plupart des lenteurs WordPress viennent en amont (PHP/DB/plugins) ou de la stratégie de cache. Le choix du serveur web est rarement le vrai goulot.
Étapes suivantes (que faire lundi matin)
- Mesurer le TTFB pour les parcours public et admin et noter les chiffres. Si vous ne pouvez pas quantifier, vous ne pouvez pas améliorer.
- Vérifier la saturation en premier : CPU, pression mémoire, latence disque. Corrigez le swap et le disque saturé avant de tuner les réglages de plugins.
- Activer les deux révélateurs (temporairement, prudemment) : slowlog PHP-FPM et slow query log MySQL.
- Corriger les gros points : autoload bloat, empreintes de requêtes lentes, appels API synchrones, et chaos WP-Cron.
- Rendre observable : file FPM, taux hit du cache, latence DB. Si vous ne le voyez pas, vous le revivrez.
La performance WordPress n’est pas mystérieuse. Elle est juste en couches. Épluchez l’oignon dans le bon ordre, et vous trouverez le goulot sans sacrifier un week-end à la superstition.