La panne WordPress la plus redoutable est celle qui se produit en silence : une mise à jour PHP « pour la sécurité » qui passe la CI, semble correcte dans un navigateur,
puis transforme votre flux de paiement le plus rentable en écran blanc à 2 h du matin pendant que l’astreignant essaie de se rappeler
quel pool est sur quel hôte. Tout le monde a des opinions. Les logs ont des faits.
L’incompatibilité de version PHP n’est pas mystérieuse. C’est juste un décalage entre le comportement d’exécution et les suppositions du code :
dans le cœur de WordPress, dans les plugins, dans les thèmes, et dans votre colle d’infrastructure (opcache, cache d’objets, proxies). Vous pouvez mettre à jour
sans interruption si vous la traitez comme un changement en production : inventaire, tests avec la vraie forme du trafic, bascule sécurisée,
et rollback sans surprise.
Ce qui casse réellement quand les versions PHP changent (et pourquoi WordPress le ressent)
Une mise à jour PHP n’est pas « juste un nouvel interpréteur ». C’est un changement dans les règles du langage, le comportement de la bibliothèque standard, la configuration par défaut,
et les extensions dont WordPress dépend. WordPress se trouve au carrefour du code hérité et de l’hébergement moderne. Il supporte d’anciens sites, des plugins de 2012,
et des développeurs qui traitent functions.php comme une confession. C’est pour cela qu’il est sensible.
Les défaillances de compatibilité viennent en quatre variantes
-
Défaillances hard : erreurs fatales, erreurs de parsing, fonctions/classes manquantes, extensions absentes.
Ce sont les moments « site indisponible ». -
Défaillances soft : warnings/notices qui deviennent fatals sous des réglages plus stricts, ou changements de logique qui
inversent le comportement (chaînes vides vs nulls, comparaisons, gestion des tableaux). -
Régressions de performance : différences d’opcache, effets secondaires du JIT, ou chemins de code qui ralentissent
à cause d’évolutions du moteur. Se manifeste généralement par une augmentation du CPU et de la latence en queue, pas par des erreurs immédiates. -
Discordances opérationnelles : valeurs par défaut différentes dans
php.ini, configs de pool FPM différentes,
paquets système manquants, permissions de fichiers modifiées, ou un serveur mis à jour et pas l’autre.
Pourquoi les mises à jour WordPress « fonctionnent en staging » et échouent en production
Le staging a souvent un ensemble de plugins différent, un cache différent, et un trafic poli. Le trafic de production est impoli.
Il frappe des endpoints étranges, envoie de gros cookies, déclenche cron aux mauvais moments, et exerce des pages d’administration rarement utilisées.
Et la plupart des environnements de staging n’exécutent pas le même cache d’objets, la même sémantique du système de fichiers, ou le même état de warm-up de l’opcode cache.
Une citation opérationnelle à garder sur un post-it : « L’espoir n’est pas une stratégie. » — idée paraphrasée couramment attribuée dans les cercles fiabilité.
Ici, espérer signifie « mettre à jour et voir ce qui se passe ». Ne le faites pas. Mesurez et basculez avec un plan.
De plus, WordPress est un écosystème de plugins. Un seul plugin peut introduire une bibliothèque fournisseur qui suppose le comportement d’une version PHP.
Vous mettez à jour PHP et soudain une dépendance lance un TypeError là où elle casait auparavant en silence. Ce n’est pas PHP qui est méchant ; c’est PHP qui devient explicite.
Votre budget d’erreur peut ne pas être d’accord.
Blague #1 : Mettre à jour PHP sans vérifier les plugins, c’est comme changer la serrure de votre porte d’entrée et être surpris que vos clés ne fonctionnent plus.
Faits intéressants et contexte historique (ce qui explique la douleur)
- PHP 7.0 (2015) a été le grand virage performance après le travail PHPNG ; beaucoup de sites WordPress ont vu des accélérations importantes sans changer de code.
- PHP 5.6 a atteint la fin de vie en 2018, mais l’hébergement mutualisé l’a gardé en vie pendant des années parce que « rien n’avait encore cassé ».
- PHP 8.0 (2020) a introduit le JIT, mais les charges typiques WordPress en tirent rarement beaucoup de bénéfice ; on se soucie surtout des améliorations du moteur et de la compatibilité.
- PHP 8 a resserré le typage et a fait en sorte que plus d’opérations lancent
TypeErrorau lieu de s’en sortir ; beaucoup de plugins hérités dépendaient de cette tolérance. - L’histoire de l’extension MySQL compte : autrefois WordPress utilisait les fonctions
mysql_*; les stacks modernes utilisentmysqliou PDO. Les anciens plugins n’ont pas toujours suivi. - Le « white screen of death » de WordPress est devenu un mème parce que les fatals étaient souvent cachés par
display_errors=Off; WordPress moderne a le mode de récupération, mais ce n’est pas une protection magique. - OPcache est devenu courant dans PHP 5.5 ; aujourd’hui, la stabilité des performances dépend de l’ajustement d’opcache et non de le thrash avec des déploiements constants.
- Composer n’a pas toujours été commun dans l’écosystème WordPress ; beaucoup de plugins embarquent encore du code fournisseur manuellement, ce qui rend le patching des dépendances et la compatibilité PHP plus chaotiques.
- FPM a remplacé mod_php comme standard pour les déploiements sérieux parce que la gestion des processus et l’isolation deviennent plus saines — aussi parce que tout exécuter dans Apache est un choix de style de vie.
Méthode de diagnostic rapide : confirmer que c’est PHP, puis trouver l’edge
Quand un site WordPress commence à renvoyer des 502, des pages vides, ou des erreurs d’administration bizarres après une « fenêtre de maintenance »,
vous avez besoin d’une boucle serrée : confirmer où se situe la défaillance, identifier le chemin de code, et décider rollback ou correction.
Ne partez pas à l’aventure.
Première étape : confirmer la couche des symptômes
-
Est-ce au niveau HTTP (502/504) ou applicatif (200 avec HTML cassé) ?
502 signifie généralement que PHP-FPM est mort, bloqué, ou que l’upstream est mal câblé. 200 avec page blanche signifie souvent une fatal PHP avec sortie supprimée. -
La défaillance est-elle sur tous les nœuds ou seulement certains ?
Un mélange de versions PHP dans une piscine équilibrée est un mode classique « ça marche parfois ». -
Est-ce limité à l’administration, au cron, ou à une route de plugin spécifique ?
Les flux de paiement et les endpoints AJAX parcourent souvent un code différent de votre page d’accueil.
Deuxième étape : vérifiez les deux logs qui comptent vraiment
- Log d’erreur PHP-FPM (ou journal systemd) : montre les fatals, segfaults, et problèmes de configuration de pool.
-
Log application WordPress/PHP :
wp-content/debug.logsi activé, ou votre centralisation de logs.
Troisième étape : isolez si c’est incompatibilité ou capacité
-
Si les erreurs mentionnent
Call to undefined function,Class not found,Parse error, ouTypeErroraprès une mise à jour : traitez-le comme une incompatibilité jusqu’à preuve du contraire. -
Si les logs montrent des timeouts,
server reached pm.max_children, ou de longues durées de requête : votre mise à jour a changé les caractéristiques de performance, et vous êtes maintenant lié par la capacité. - Si les défaillances sont intermittentes et corrélées à un backend spécifique : vous avez probablement un skew de version ou de configuration.
Quatrième étape : décider rollback vs correction en avant
Ma règle : si le checkout/l’administration est en panne ou si les taux d’erreur explosent et que vous n’avez pas de correction confirmée en main, rollback d’abord.
Un rollback vous achète du temps de réflexion sans que vos clients paient votre curiosité.
Tâches pratiques (commandes, sorties et décisions)
Voici les tâches que j’exécute réellement. Pas parce qu’elles sont sophistiquées, mais parce qu’elles éliminent l’ambiguïté.
Chaque tâche inclut : commande, ce que signifie la sortie, et la décision que vous prenez.
Task 1: Identify the PHP version serving the site (CLI vs FPM can differ)
cr0x@server:~$ php -v
PHP 8.1.2 (cli) (built: Feb 15 2025 10:41:12) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.2, Copyright (c) Zend Technologies
with Zend OPcache v8.1.2, Copyright (c), by Zend Technologies
Signification : Il s’agit du binaire CLI. Il peut ne pas correspondre à ce que Nginx/Apache utilise via FPM.
Décision : Si vous déboguez un comportement web, ne présumez pas qu’il s’agit du runtime. Vérifiez FPM ensuite.
Task 2: Confirm PHP-FPM version and status via systemd
cr0x@server:~$ systemctl status php8.1-fpm --no-pager
● php8.1-fpm.service - The PHP 8.1 FastCGI Process Manager
Loaded: loaded (/lib/systemd/system/php8.1-fpm.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2025-12-27 10:12:33 UTC; 2h 4min ago
Docs: man:php-fpm8.1(8)
Main PID: 1234 (php-fpm8.1)
Status: "Processes active: 6, idle: 10, Requests: 24912, slow: 3, Traffic: 1.2req/sec"
Signification : FPM fonctionne et rapporte des requêtes lentes.
Décision : Si vous voyez « failed » ou des boucles de redémarrage, vous êtes en territoire d’incident. Si les requêtes lentes augmentent après la mise à jour, enquêtez sur la performance et les timeouts.
Task 3: Verify what upstream socket Nginx is using (catch version skew)
cr0x@server:~$ sudo nginx -T 2>/dev/null | sed -n '/fastcgi_pass/p' | head
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
Signification : Nginx est connecté au socket PHP 8.1.
Décision : Si ceci pointe vers php7.4-fpm.sock sur certains nœuds et php8.1-fpm.sock sur d’autres, corrigez votre gestion de configuration avant de toucher au code.
Task 4: Confirm Apache PHP handler (mod_php vs proxy_fcgi)
cr0x@server:~$ apachectl -M 2>/dev/null | egrep 'php|proxy_fcgi|mpm'
mpm_event_module (shared)
proxy_fcgi_module (shared)
Signification : Apache utilise probablement FPM via proxy_fcgi, pas mod_php.
Décision : Si vous voyez un php_module chargé, vous êtes sur mod_php et le changement de version est différent et plus risqué. Préférez l’isolation FPM pour les environnements multi-versions.
Task 5: Hit a local phpinfo-style endpoint safely (without exposing it publicly)
cr0x@server:~$ curl -sS -H 'Host: example.com' http://127.0.0.1/wp-admin/admin-ajax.php | head
0
Signification : Cela ne montre pas la version PHP, mais confirme que WordPress s’exécute via le chemin web local.
Décision : Si cela renvoie 502/504 en local, le problème est sur le chemin serveur/app, pas sur le CDN ou le DNS externe.
Task 6: Enable WordPress debugging (temporarily) and confirm log writes
cr0x@server:~$ sudo -u www-data php -r 'echo "ok\n";'
ok
Signification : Votre utilisateur web peut exécuter PHP et devrait pouvoir écrire des logs si les permissions le permettent.
Décision : Si l’utilisateur web ne peut pas exécuter ou écrire là où il faut, corrigez les permissions avant de chasser des fantômes de « compatibilité ».
Task 7: Tail PHP-FPM logs for fatals and pool errors during a request
cr0x@server:~$ sudo tail -n 30 /var/log/php8.1-fpm.log
[27-Dec-2025 12:14:22] WARNING: [pool www] server reached pm.max_children setting (20), consider raising it
[27-Dec-2025 12:14:27] ERROR: WARNING: [pool www] child 1842 said into stderr: "PHP Fatal error: Uncaught TypeError: strlen(): Argument #1 ($string) must be of type string, null given in /var/www/html/wp-content/plugins/foo/bar.php:91"
Signification : Vous avez à la fois une pression de capacité (pm.max_children) et une vraie incompatibilité (TypeError).
Décision : Corrigez le fatal d’abord (c’est la correction). Ensuite traitez la capacité, parce que les erreurs peuvent amplifier la charge via des retries.
Task 8: Confirm installed PHP modules (missing extensions cause weirdness)
cr0x@server:~$ php -m | egrep 'curl|gd|imagick|mbstring|mysqli|openssl|zip'
curl
gd
mbstring
mysqli
openssl
zip
Signification : Des modules comme imagick peuvent manquer après une mise à jour, modifiant le comportement du traitement média.
Décision : Si une extension requise est absente, installez-la pour la version PHP cible et redémarrez FPM avant d’incriminer WordPress.
Task 9: Check WordPress core and plugin versions with WP-CLI
cr0x@server:~$ cd /var/www/html
cr0x@server:/var/www/html$ sudo -u www-data wp core version
6.4.3
Signification : Vous avez la version du cœur identifiée.
Décision : Si le cœur est ancien par rapport à votre PHP cible, mettez à jour le cœur en staging d’abord. La compatibilité du cœur est généralement meilleure que celle des plugins, mais ne pariez pas là-dessus.
Task 10: Find the plugins most likely to break (and disable one without touching the DB manually)
cr0x@server:/var/www/html$ sudo -u www-data wp plugin list --status=active
+-----------------------+----------+-----------+---------+
| name | status | update | version |
+-----------------------+----------+-----------+---------+
| woocommerce | active | available | 8.1.1 |
| elementor | active | none | 3.18.0 |
| foo-payments-gateway | active | none | 2.4.7 |
+-----------------------+----------+-----------+---------+
Signification : Vous avez maintenant une liste courte de code qui s’exécute sur la plupart des requêtes.
Décision : Si un fatal pointe dans un chemin de plugin, désactivez ce plugin sur un nœud test ou en staging d’abord. En urgence, désactivez en production uniquement si vous comprenez l’impact business.
Task 11: Run a PHP syntax check across a plugin or theme (catches parse errors early)
cr0x@server:/var/www/html$ find wp-content/plugins/foo-payments-gateway -name '*.php' -print0 | xargs -0 -n1 php -l | head
No syntax errors detected in wp-content/plugins/foo-payments-gateway/includes/api.php
No syntax errors detected in wp-content/plugins/foo-payments-gateway/foo.php
Signification : Pas d’erreurs de parsing. C’est nécessaire, pas suffisant.
Décision : Si vous voyez des erreurs de parsing, ce plugin est mort sur cette version PHP. Mettez-le à jour/remplacez-le, ou ne mettez pas à jour PHP pour l’instant.
Task 12: Detect deprecated calls and warnings by running a page under stricter error reporting (staging)
cr0x@server:~$ php -d display_errors=1 -d error_reporting=E_ALL -r 'trigger_error("test", E_USER_DEPRECATED);'
Deprecated: test in Command line code on line 1
Signification : Vous pouvez forcer la visibilité des dépréciations dans un environnement contrôlé.
Décision : En staging, augmentez la visibilité des erreurs et exécutez les flux critiques. Les dépréciations peuvent devenir les fatals de demain quand les bibliothèques évolueront.
Task 13: Check live traffic errors from access logs (spot 502 spikes and failing endpoints)
cr0x@server:~$ sudo awk '$9 ~ /^5/ {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
184 /wp-admin/admin-ajax.php
77 /?wc-ajax=checkout
22 /wp-json/wc/v3/orders
Signification : Les échecs se concentrent autour des AJAX et du checkout — des chemins typiques lourds en plugins.
Décision : Concentrez les tests et l’isolation sur ces routes. Ne perdez pas de temps à rafraîchir la page d’accueil.
Task 14: Confirm opcache settings (mis-tuned opcache can look like “PHP upgrade broke it”)
cr0x@server:~$ php -i | egrep 'opcache.enable|opcache.memory_consumption|opcache.max_accelerated_files' | head
opcache.enable => On => On
opcache.memory_consumption => 128 => 128
opcache.max_accelerated_files => 10000 => 10000
Signification : OPcache est activé, mais la mémoire peut ou non suffire selon l’étendue des plugins.
Décision : Si vous voyez des resets fréquents du cache ou « out of memory for opcache », augmentez la mémoire opcache et les emplacements de fichiers. Sinon vous courrez après une latence imaginaire.
Task 15: Compare PHP versions across a fleet (catch partial upgrades)
cr0x@server:~$ for h in wp-app-01 wp-app-02 wp-app-03; do ssh $h "php-fpm8.1 -v 2>/dev/null | head -1 || php-fpm8.0 -v 2>/dev/null | head -1"; done
PHP 8.1.2 (fpm-fcgi) (built: Feb 15 2025 10:41:12)
PHP 8.1.2 (fpm-fcgi) (built: Feb 15 2025 10:41:12)
PHP 8.0.30 (fpm-fcgi) (built: Jan 10 2025 08:12:44)
Signification : Un nœud est sur PHP 8.0 tandis que les autres sont sur 8.1.
Décision : Arrêtez. Corrigez le skew de versions avant d’investiguer des « erreurs aléatoires ». Les runtimes mixtes sont une taxe de fiabilité que vous payez éternellement.
Task 16: Verify what config file FPM is using (because you think you changed it…)
cr0x@server:~$ php-fpm8.1 -tt 2>&1 | head -20
[27-Dec-2025 12:33:01] NOTICE: configuration file /etc/php/8.1/fpm/php-fpm.conf test is successful
[27-Dec-2025 12:33:01] NOTICE: using php.ini in /etc/php/8.1/fpm/php.ini
Signification : Vous modifiez le bon chemin de fichier pour FPM.
Décision : Si cela pointe ailleurs (containers, préfixes personnalisés), ajustez vos outils de déploiement. Éditer le mauvais ini file est un classique éternel.
Task 17: Validate PHP-FPM pool capacity and timeouts (avoid “upgrade caused 502” myths)
cr0x@server:~$ sudo egrep 'pm\.max_children|pm\.max_requests|request_terminate_timeout' /etc/php/8.1/fpm/pool.d/www.conf | sed 's/;.*$//'
pm.max_children = 20
pm.max_requests = 500
request_terminate_timeout = 60s
Signification : Votre pool est limité à 20 workers et tue les requêtes après 60 secondes.
Décision : Si le trafic ou le code plus lent a augmenté le temps de requête, augmentez max_children (dans les limites CPU/RAM) et ajustez request_terminate_timeout. Mais ne « résolvez » pas des erreurs fatales en augmentant les timeouts.
Schémas de mise à niveau qui évitent les interruptions (Nginx/Apache, PHP-FPM, et rollbacks)
« Sans interruption » ne veut pas dire « sans risque ». Cela signifie que vous pouvez changer de runtime sans perdre des connexions ni rendre le site inaccessible.
L’astuce est la parallélisation : exécuter l’ancien et le nouveau PHP côte à côte, router le trafic intentionnellement, et garder le rollback à un changement de configuration près.
Pattern A: Parallel PHP-FPM sockets + phased traffic shift
Exécutez deux services FPM : ancien (ex. PHP 8.0) et nouveau (ex. PHP 8.1). Connectez Nginx (ou Apache proxy_fcgi) à un socket à la fois,
ou mieux, fractionnez le trafic au niveau du load balancer : canarisez un nœud sur le nouveau PHP, gardez le reste sur l’ancien.
C’est l’approche la plus propre pour un quasi-zéro downtime parce que :
- Elle découple l’installation des paquets du basculement du trafic.
- Elle permet de tester le vrai trafic de production sur un petit échantillon.
- Le rollback est rapide : changez le socket upstream ou retirez le canari du pool.
Pattern B: Blue/green app tier with immutable images
Construisez une nouvelle image (VM ou container) avec le nouveau runtime PHP, mêmes configs, même code WordPress et plugins, puis montez-la
derrière le load balancer à côté de l’ancien pool. Drainer et basculer. C’est ainsi que vous gardez les changements audités.
Cela empêche aussi les « serveurs flocon de neige » où un nœud a une extension différente parce que quelqu’un a debuggué une fois et ne l’a jamais documenté.
Pattern C: Same servers, but fast rollback via config toggle
Si vous devez switcher in place, gardez l’ancien service FPM installé et démarrable, et traitez la bascule comme un déploiement de configuration :
mettez à jour Nginx fastcgi_pass vers le nouveau socket et faites un reload. Reload, pas restart.
Reload signifie que les connexions existantes survivent ; les nouveaux workers prennent la config. Restart signifie que vous apprenez ce que valent les clients mécontents.
Choisissez reload. Toujours.
Le plan de rollback minimum sûr
- Le vieil PHP-FPM reste installé et en fonctionnement (ou démarrable) pendant toute la fenêtre de mise à jour.
- Votre serveur web peut basculer entre sockets ou upstreams rapidement.
- OPcache est réinitialisé lors du switch (ou vous acceptez le risque d’un mélange de bytecode mis en cache).
- Vous savez quels plugins/thèmes ont changé récemment, et vous pouvez les désactiver avec WP-CLI.
Blague #2 : La seule chose plus permanente qu’une mise à jour PHP temporaire est le hotfix « temporaire » que vous aurez peur de supprimer.
Stockage et état : le multiplicateur caché de downtime
WordPress n’est pas que PHP. C’est PHP plus une base de données plus des uploads plus des caches. Une mise à jour PHP peut déclencher :
- Des stampedes de cache si opcache se réinitialise et que les caches d’objets ratent en même temps.
- Des changements de régénération média si les bibliothèques d’image diffèrent (disponibilité de gd vs imagick).
- Des variations de permissions sur le système de fichiers si les paquets changent les defaults user/group ou les configs de pool.
Si vous exécutez un stockage partagé (NFS, EFS, Gluster, CephFS), soyez prudent : un plugin effectuant beaucoup d’appels stat() peut
devenir plus lent sous un nouveau runtime PHP (ou simplement avec un état d’opcache différent), et la latence de stockage devient soudain critique.
En production, le stockage n’est jamais « le problème de quelqu’un d’autre ». C’est juste un problème qui n’a pas encore appris votre numéro de téléphone.
Trois mini-histoires du monde corporate (douleur, orgueil et compétence ennuyeuse)
Mini-histoire #1 : L’incident causé par une mauvaise hypothèse
Une entreprise de taille moyenne gérait WordPress pour des pages marketing et une app séparée pour la facturation. Le marketing n’était pas « critique »,
ce qui est une phrase que les dirigeants prononcent juste avant de demander un ETA toutes les sept minutes.
L’équipe infra a mis à jour PHP de 7.4 à 8.1 sur les nœuds web pendant un cycle de patch de routine. Ils ont validé php -v, rafraîchi la page d’accueil, vu un 200, et fermé le ticket. L’hypothèse était simple : « Si la page d’accueil charge, WordPress va bien. »
Le lendemain matin, l’équipe contenu a essayé de publier un article. L’éditeur a planté. Puis la connexion admin a commencé à renvoyer des 500.
La page d’accueil semblait toujours correcte parce qu’elle était mise en cache par le CDN et le cache edge ne se souciait pas des fatals PHP.
Pendant ce temps, les posts programmés ne se publiaient pas parce que les appels WP-Cron échouaient, et un lancement de campagne manquait discrètement sa fenêtre.
La cause racine était un plugin utilisé uniquement dans le chemin admin de l’éditeur. Il utilisait une bibliothèque qui déclenchait un TypeError
sous PHP 8.1 lorsqu’on lui passait null. Il ne s’exécutait jamais sur les requêtes anonymes de la page d’accueil. Personne n’a testé le chemin admin. Personne n’a regardé les logs. Tout le monde a testé la page la plus susceptible d’être en cache.
La correction a été embarrassante de simplicité : fixer la version du plugin à une version mise à jour et ajouter un test « chemin critique admin » à la checklist de release.
La vraie leçon était opérationnelle : validez les workflows qui comptent, pas les pages qui sont pratiques.
Mini-histoire #2 : L’optimisation qui s’est retournée contre eux
Une autre organisation voulait « accélérer WordPress » et a décidé d’activer des réglages OPcache agressifs et d’augmenter pm.max_children juste après la mise à jour PHP. Deux changements à la fois. Deux boutons, un tableau de bord. Vous pouvez deviner la fin.
Initialement, les métriques avaient l’air excellentes : plus de débit, moins de pics CPU. Puis la pression mémoire est apparue. Les nœuds ont commencé à swapper.
La latence est devenue non linéaire. Quelques 502 sont apparus, puis beaucoup. L’équipe a supposé que la nouvelle version PHP était instable.
Ce n’était pas le cas. Ils avaient augmenté le nombre de workers sans augmenter la mémoire, et OPcache était configuré assez grand pour que chaque nœud ait moins de marge pour la charge réelle des requêtes.
Sous charge, le kernel a commencé à récupérer de la mémoire, le cache de pages a été churné, et les workers PHP se sont figés. La stack ne « crashait » pas. Elle s’est étouffée.
Le rollback vers l’ancienne version PHP n’a pas aidé parce que les réglages de ressources sont restés en place. Cela a créé de la confusion :
« On a rollbacké mais c’est toujours cassé. » Après une heure d’investigation, ils ont remis les réglages FPM et OPcache et le site a récupéré.
La leçon : le tuning de performance a un rayon d’impact. Faites-le séparément d’une mise à jour de version. Et si vous touchez pm.max_children, mieux vaut connaître l’empreinte mémoire par worker, pas seulement l’utilisation CPU.
Mini-histoire #3 : La pratique ennuyeuse mais correcte qui a sauvé la journée
Une troisième entreprise exécutait WooCommerce avec beaucoup de plugins, certains antiques, certains sur mesure. Ils avaient une habitude bureaucratique : chaque mise à jour de runtime nécessitait un nœud canari et un test synthétique scripté des « 20 endpoints principaux ».
Aucune exception.
La mise à jour PHP vers 8.2 a été staged. Ils ont construit de nouvelles images, lancé un seul nœud canari, et routé 2 % du trafic vers lui.
Les tests synthétiques frappaient le checkout, l’ajout au panier, la connexion, la sauvegarde d’un post en admin, et quelques endpoints API. Ils ont aussi reproduit les patrons de trafic réels en rejouant un petit échantillon des logs d’accès sur le canari (sanitisé et rate-limité).
En quelques minutes, les logs d’erreur du canari ont montré des warnings devenant des fatals à l’intérieur d’un plugin de shipping en production. Le pool principal est resté sain.
L’équipe a désactivé le plugin sur le canari pour confirmer le diagnostic, puis a retiré le canari de la rotation.
Les clients n’ont rien remarqué.
La partie « ennuyeuse » était le runbook : les commandes exactes pour diff les configs, comparer les listes de modules, valider les réglages de pool,
et revenir sur les poids de trafic. Ce n’était pas malin. C’était répétable. C’est ce qui les a sauvés.
Leur correction ultérieure était aussi ennuyeuse : remplacer le plugin de shipping par une alternative maintenue et replanifier la montée de version PHP.
Ils ont perdu une journée, pas un week-end.
Erreurs courantes : symptôme → cause racine → correction
Si vous avez été en production assez longtemps, vous pouvez diagnostiquer la moitié d’entre elles à partir d’une seule capture d’écran. L’autre moitié exige des logs.
Voici la cartographie.
1) Symptom: White screen (200 OK, blank page)
- Cause racine : Erreur fatale PHP avec
display_errors=Off, souvent lors de l’initialisation d’un thème ou plugin. - Correction : Consultez le log PHP-FPM pour les fatals. Activez temporairement le debug WordPress. Identifiez et mettez à jour/désactivez le plugin/thème fautif.
2) Symptom: 502 Bad Gateway right after upgrade
- Cause racine : Nginx/Apache pointe encore vers l’ancien chemin socket, ou FPM ne tourne pas, ou le pool ne peut pas démarrer à cause d’une syntaxe de config.
- Correction : Confirmez la cible
fastcgi_pass, exécutezphp-fpm -tt, vérifiez systemd status, corrigez les permissions du socket, reload du serveur web.
3) Symptom: Works sometimes, fails sometimes
- Cause racine : Skew de versions sur les nœuds ; un serveur mis à jour, les autres non. Ou fichiers de plugin incohérents sur un stockage partagé.
- Correction : Comparez les versions d’exécution sur tous les nœuds. Assurez des déploiements atomiques et identiques ; ne mélangez pas des modifications manuelles NFS avec du CI.
4) Symptom: Admin dashboard broken, public site fine
- Cause racine : Plugin incompatible utilisé principalement dans les chemins admin/éditeur. Le CDN masque les échecs publics.
- Correction : Testez les workflows admin en staging. Surveillez les logs du canari. Désactivez/ouvrez les plugins admin en priorité.
5) Symptom: Checkout fails, cart page fine
- Cause racine : Incompatibilité du gateway de paiement ou plugin d’expédition ; le typage plus strict de PHP 8 provoque des fatals dans des cas rares.
- Correction : Reproduisez via
/?wc-ajax=checkout, lisez les erreurs PHP, mettez à jour le plugin ou remplacez-le. Ne « réglez » pas ça en augmentant les timeouts.
6) Symptom: Sudden CPU spike and slow requests after upgrade
- Cause racine : Cold start d’OPcache, mauvaise configuration du JIT, ou efficacité réduite du cache due à des resets ; parfois un plugin emprunte un chemin plus lent sous le nouveau PHP.
- Correction : Warm-up des caches, confirmez les réglages opcache, analysez le slowlog, comparez les profils de requêtes. Ajustez le pool FPM seulement après que la correction soit faite.
7) Symptom: Image uploads fail or thumbnails missing
- Cause racine :
gdouimagickmanquant sur la nouvelle version PHP ; ou des versions de librairie différentes changent le comportement. - Correction : Installez les extensions correspondantes, redémarrez FPM, vérifiez avec
php -m, puis retestez les uploads.
8) Symptom: “Allowed memory size exhausted” appears more often
- Cause racine : Limites mémoire par défaut différentes dans l’ini FPM ; comportement de plugin changeant ; la concurrence accrue augmente la mémoire maximale.
- Correction : Confirmez
memory_limitpour FPM, ajustez-le selon les contraintes réelles, et auditez le plugin qui alloue massivement. Ne colmatez pas les fuites avec une mémoire infinie.
Listes de contrôle / plan étape par étape
Voici le plan que je lancerais pour un site WordPress en production où le downtime est inacceptable et la responsabilité abondante.
Suivez l’ordre. L’ordre est tout.
Phase 0: Decide what “no downtime” means for you
- Objectif : Pas de page de maintenance planifiée ; les sessions existantes survivent ; une brève augmentation de latence en queue est acceptable.
- Non négociable : rollback en minutes, canari, et visibilité des logs.
- Vérification de réalité : Si vous avez un seul serveur sans load balancer, « pas de downtime » devient « downtime très court ». Vous pouvez toujours être prudent, mais la physique gagne.
Phase 1: Inventory the runtime, modules, and WordPress surface area
- Enregistrez la version PHP-FPM, le chemin ini, la liste des modules, les réglages opcache.
- Exportez la liste des plugins et le thème.
- Identifiez les flux critiques : login, sauvegarde admin, checkout, endpoints API, cron.
- Confirmez que vous pouvez désactiver des plugins via WP-CLI si wp-admin devient inaccessible.
Phase 2: Build a staging environment that is not a fairy tale
- Même code WordPress, mêmes plugins, même thème.
- Mêmes couches de cache (cache d’objets, comportement de page cache), ou au moins comprenez les différences.
- Copiez la base de données de production (sanitisée) et un échantillon représentatif des uploads.
- Utilisez la même classe de configuration PHP-FPM (timeouts, réglages de pool) pour attraper les problèmes de capacité tôt.
Phase 3: Compatibility testing that actually finds problems
- Exécutez les endpoints principaux et les workflows critiques sous le nouveau PHP.
- Transformez les warnings en signal en staging : error_reporting élevé, capture des logs.
- Surveillez les TypeErrors, les warnings de dépréciation dans les chemins clés, les extensions manquantes.
- Exécutez un scan syntaxique ciblé et des smoke tests sur les plugins à risque connus (paiement, expédition, builders).
Phase 4: Canary in production
- Montez un nœud avec le nouveau PHP (ou basculez un nœud existant).
- Routez un faible pourcentage de trafic vers lui.
- Surveillez : taux 5xx, latence, redémarrages PHP-FPM, slowlog, utilisation mémoire.
- Si des erreurs apparaissent : retirez le canari de la rotation, corrigez en staging, réessayez.
Phase 5: Rollout and post-switch stabilization
- Déployez progressivement sur l’ensemble du parc.
- Warm-up des caches pour éviter les stampedes.
- Gardez l’ancien PHP disponible jusqu’à ce que vous ayez traversé un cycle business complet (pas seulement 15 minutes calmes).
- Après stabilisation : retirez l’ancien runtime, mais conservez la documentation rollback (comment réinstaller vite, quels paquets).
FAQ
1) How do I know if a WordPress issue is PHP incompatibility or just a capacity problem?
Cherchez des fatals et des TypeErrors dans les logs PHP-FPM. Les problèmes de capacité se manifestent par des timeouts, « reached pm.max_children », des requêtes lentes, et des 502 sans trace de pile spécifique.
L’incompatibilité laisse presque toujours une miette : chemin de fichier, numéro de ligne, et un type d’erreur clair.
2) Can I upgrade PHP without upgrading WordPress core?
Parfois, oui, mais c’est un mauvais pari. Le cœur suit généralement les versions PHP supportées, mais un cœur ancien avec un PHP moderne augmente le risque que les plugins rencontrent des chemins non testés.
Mettez à jour le cœur en staging d’abord, puis PHP. Si vous ne pouvez pas, au moins validez votre version exacte du cœur contre le PHP cible en staging avec des workflows réels.
3) Why does WP-CLI work but the website fails?
WP-CLI utilise le SAPI CLI et son propre chemin php.ini. Votre site utilise PHP-FPM avec des réglages ini différents, d’autres extensions, et des permissions d’utilisateur différentes.
Vérifiez toujours les deux. La différence n’est pas académique ; c’est là où résident les incidents.
4) Is it safe to run two PHP-FPM versions on the same server?
Oui, si vous isolez les sockets/ports et les configs par version. C’est une pratique opérationnelle courante pour les mises à niveau staged.
Le risque est l’erreur opérateur : pointer Nginx vers le mauvais socket, ou installer des modules sur une version et oublier l’autre. Utilisez la gestion de configuration et des chemins explicites.
5) What’s the fastest rollback if PHP 8.x breaks a plugin?
Si vous avez gardé l’ancien service FPM en fonctionnement : repointez le socket upstream vers l’ancien et rechargez Nginx/Apache. C’est une question de minutes.
Si vous ne l’avez pas fait : réinstallez les paquets, reconfigurez, redémarrez les services—là c’est un incident, pas un changement.
6) Should I enable display_errors in production to see what’s happening?
Non. Logguez les erreurs à la place. Afficher les erreurs peut divulguer des secrets et casser les réponses de manière inesthétique.
Utilisez les logs PHP-FPM, le debug log WordPress (temporairement), et la centralisation des logs. Si vous avez besoin de visibilité, ajoutez-la de façon sûre.
7) Why do I get 502s only during traffic spikes after the upgrade?
Votre nouveau runtime peut avoir des caractéristiques de performance différentes, ou vos caches se sont réinitialisés et vous subissez une pénalité de cold-start.
Sous charge, de petits ralentissements deviennent du queueing, puis des timeouts. Vérifiez les réglages du pool FPM (pm.max_children), le slowlog, et les timeouts upstream.
8) Do I need to clear OPcache when switching PHP versions?
Si vous basculez de socket entre deux services FPM, chacun a son propre état OPcache—pas de contamination croisée.
Si vous redémarrez ou rechargez le même service après des changements de code, réinitialiser OPcache peut éviter des comportements étranges « ancien code encore en mémoire ».
Mais ne détruisez pas aveuglément les caches sur tout le parc ; cela peut causer un stampede.
9) What plugin types are the most dangerous during PHP upgrades?
Les passerelles de paiement, plugins d’expédition/taxes, page builders, plugins de sécurité/firewall, et tout ce qui embarque de lourdes bibliothèques fournisseur.
Ils interviennent profondément dans les chemins de requête et ont souvent des hypothèses strictes sur les types et l’environnement serveur.
10) If I can’t do a canary, what’s the next best thing?
Au minimum : exécutez l’ancien et le nouveau PHP-FPM côte à côte sur le même hôte et basculez via une config reloadable, avec un rollback testé.
Si vous êtes sur un seul nœud, planifiez une fenêtre de faible trafic et acceptez que « pas de downtime » devienne « downtime court », puis réduisez le risque avec des tests de staging exhaustifs.
Conclusion : étapes pratiques suivantes
L’incompatibilité PHP n’est pas une faute morale. C’est le résultat prévisible d’exécuter un large écosystème de plugins sur un runtime qui améliore constamment sa correction.
Votre travail est de rendre la mise à jour ennuyeuse : inventaire, staging, canari, bascule, et rollback rapide si nécessaire.
- Aujourd’hui : Confirmez votre vrai runtime web (version FPM, câblage socket) et centralisez les logs dont vous aurez besoin en cas d’incident.
- Cette semaine : Construisez un environnement de staging qui reflète les plugins, le caching et les workflows de production. Testez les chemins admin et checkout, pas seulement la page d’accueil.
- Prochaine fenêtre de changement : Exécutez des versions PHP-FPM parallèles et canarisez un nœud. Surveillez les logs d’erreur et la latence en queue. Avancez seulement si les signaux sont propres.
- Après le déploiement : Éliminez le skew de versions, documentez le rollback, et arrêtez de faire « deux changements en même temps ». Votre futur vous a assez de hobbies.