Apache pour WordPress : modules et règles qui plantent les sites (et comment les réparer)

Cet article vous a aidé ?

La plupart des pannes WordPress sous Apache ne commencent pas avec WordPress. Elles commencent par « un petit changement Apache » qui avait l’air inoffensif :
une nouvelle règle de sécurité, un réglage de compression, un « nettoyage » de redirections, ou un en-tête de cache malin copié depuis un billet de 2014.
Puis votre page d’accueil se redirige elle-même, l’administration devient une exhibition 403, ou les appels à l’API REST échouent mystérieusement seulement le mardi.

C’est un guide de terrain pour les environnements de production : quels modules Apache et quels motifs de configuration cassent couramment WordPress, comment prouver que c’est bien cela,
et comment le réparer sans tergiverser. Si vous opérez WordPress à grande échelle, ceci est moins « bases du serveur web » et plus « arrêter l’hémorragie, puis prévenir ».

Procédure de diagnostic rapide

Quand WordPress « casse » sous Apache, le chemin le plus rapide est de classer la panne par symptôme HTTP,
puis de confirmer la couche responsable (Apache vs PHP-FPM vs WordPress vs réseau/CDN).
Ne devinez pas. Faites parler le serveur.

Première étape : identifier la classe de panne en 90 secondes

  1. Obtenez une réponse propre depuis l’origine (passez le CDN si présent). Utilisez curl en verbose et suivez les redirections.
    Vous cherchez des patterns de codes de statut et des chaînes de redirections.
  2. Vérifiez les logs d’erreur Apache pour le même horodatage. Une seule ligne nomme souvent le module coupable (security2, rewrite, proxy_fcgi).
  3. Vérifiez les champs du journal d’accès : statut, octets, temps de requête, temps upstream (si journalisé), user agent.
    Si vous ne journalisez pas le temps de requête, commencez aujourd’hui.

Deuxième étape : décidez dans lequel de ces quatre compartiments vous êtes

  • Boucle de redirection / mauvais schéma / mauvais hôte : presque toujours des règles de réécriture, des en-têtes de proxy, ou un décalage siteurl/home de WordPress.
  • 403 Forbidden : mod_security, permissions système de fichiers, ou règles d’authz Apache (Require, AllowOverride, Options).
  • 500/502/503 : mauvais wiring du gestionnaire PHP (proxy_fcgi), incompatibilité MPM/PHP, timeouts, ou limites mémoire.
  • Lent ou instable : KeepAlive, cas limites HTTP/2, compression, en-têtes de cache, saturation du backend, IO disque, ou problèmes DNS/proxy.

Troisième étape : confirmez avec un test ciblé

Choisissez le test qui réduit l’incertitude :
désactivez un module sur un vhost staging, contournez la réécriture pour un chemin, lancez une requête avec instrumentation de niveau RewriteLog,
ou rejouez une requête contre le backend directement.

Une citation opérationnelle à graver dans votre runbook, parce qu’elle est vraie sous pression :
« L’espoir n’est pas une stratégie. » — Gene Kranz

Faits intéressants et contexte historique (utile, pas triviaux)

  • .htaccess existe principalement pour l’hébergement mutualisé : il permet aux utilisateurs de contrôler la configuration par répertoire sans accès root. C’est pratique et coûteux en temps d’exécution.
  • mod_php a poussé Apache vers prefork : pendant des années, exécuter PHP dans Apache impliquait que prefork MPM était le choix sûr. Beaucoup d’hébergeurs WordPress portent encore cet héritage.
  • Les « permaliens jolis » de WordPress sont essentiellement des règles de réécriture : les règles canoniques ont été écrites pour mod_rewrite d’abord, puis adaptées ailleurs. Apache est la forme de référence.
  • mod_security est devenu courant dans les années 2000 : il est puissant, et ses faux positifs contre l’administration WordPress et XML-RPC sont légendaires.
  • HTTP/2 dans Apache a mûri avec le temps : les premières déploiements ont heurté des limites de compatibilité avec certains clients et intermédiaires. Aujourd’hui c’est surtout stable, mais les mauvaises configurations font encore des dégâts.
  • Le support de Brotli est arrivé plus tard que gzip : mod_brotli est plus récent, et d’anciens proxies/caches peuvent mal se comporter si les en-têtes Vary ne sont pas corrects.
  • La syntaxe authz d’Apache a changé : l’ancienne ère « Order allow,deny » a fait place à « Require all granted ». Mixer des versions ou recopier des snippets sans comprendre provoque des 403 accidentels.
  • « AllowOverride None » est un bon conseil performance : désactiver l’analyse de .htaccess améliore les performances, mais cela cassera les permaliens WordPress à moins de déplacer les règles dans le vhost.

Les éléments qui cassent le plus souvent : modules et règles nuisibles pour WordPress

1) mod_rewrite : l’auteur silencieux de vos boucles de redirection

WordPress a besoin de règles de réécriture pour les permaliens jolis. Le problème n’est pas mod_rewrite lui-même ; ce sont les humains.
Concrètement : des humains qui mélangent redirections (301/302), enforcement d’hôte canonique, HTTP→HTTPS, et nettoyage de slash final
à plusieurs niveaux (Apache + WordPress + CDN + load balancer).

Patterns de casse typiques :

  • Boucle infinie vers la même URL : deux règles ne sont pas d’accord sur le schéma ou l’hôte, ou WordPress pense être en HTTP alors que les clients arrivent en HTTPS.
  • L’administration redirige vers la page d’accueil : WordPress voit un hôte/schéma différent du navigateur à cause d’en-têtes proxys manquants.
  • REST API 404 : les règles de réécriture ne s’appliquent pas à /wp-json parce que AllowOverride bloque .htaccess, ou parce qu’une règle court-circuite.

Avis pratique : si vous avez l’accès root, gardez les règles de réécriture WordPress hors de .htaccess et dans le vhost. Moins de statfs, moins de surprises.
Mais si vous faites cela, traitez-le comme du code : versionnez-le, testez-le, et documentez les invariants (noms d’hôtes, schémas et chemins canoniques).

2) AllowOverride et Options : « ça marche en staging » n’est pas une configuration

La cause la plus courante de « les permaliens WordPress ont cassé » sur Apache n’est pas WordPress du tout. C’est Apache qui ignore les règles.
Cela arrive quand vous mettez :

  • AllowOverride None (donc .htaccess est ignoré)
  • ou que vous overridez Options / Require d’une manière qui bloque l’accès.

La « bonne » correction est soit d’autoriser les types d’override spécifiques dont WordPress a besoin (généralement AllowOverride FileInfo au minimum),
soit de déplacer les règles de réécriture dans le vhost et de garder AllowOverride None pour la performance.

3) mod_security (security2) : une protection qui bloque fréquemment l’administration

mod_security est un pare-feu applicatif web. Il inspecte les requêtes et bloque les patterns qui paraissent malveillants.
Le trafic d’administration WordPress ressemble à du malveillant un bon jour : beaucoup de paramètres, données sérialisées, blobs JSON, et des plugins aux chaînes de requête « créatives ».

Modes d’échec classiques :

  • 403 sur wp-admin après l’installation d’un plugin ou l’activation d’un constructeur de pages.
  • Requêtes REST API bloquées, causant des échecs de l’éditeur par blocs, des embeds cassés, ou des erreurs AJAX.
  • Déconnexions aléatoires parce que certains cookies/en-têtes déclenchent des règles.

Conseil tranché : ne désactivez pas mod_security globalement. Réglez-le par vhost et par ID de règle, et conservez un journal d’audit que vous lisez réellement.
« Nous avons désactivé le WAF » est une correction à court terme qui devient un rapport d’incident à long terme.

4) mod_proxy_fcgi et PHP-FPM : 502 qui ressemblent à WordPress mais ne le sont pas

Apache moderne + WordPress signifie souvent qu’Apache sert les assets statiques et proxy les requêtes PHP vers PHP-FPM via proxy_fcgi.
Les mauvaises configurations ici produisent :

  • 502 Bad Gateway (FPM down, permissions de socket, mauvais chemin, timeout)
  • 503 Service Unavailable (backend saturé, max children atteint)
  • 500 intermittents (crashs, épuisement mémoire, requêtes lentes atteignant des timeouts)

5) Choix de MPM (event/worker/prefork) : tuning performance qui casse sous charge

Le Multi-Processing Module (MPM) d’Apache décide comment il gère les connexions.
WordPress lui-même s’en fiche, mais votre intégration PHP et la forme de votre trafic non.

  • prefork : modèle ancien processus-par-connexion ; fonctionne avec mod_php ; gourmand en mémoire.
  • worker : threads ; meilleure concurrence ; nécessite des modules thread-safe.
  • event : meilleur usage général pour trafic keep-alive ; s’associe bien avec PHP-FPM.

La casse vient des mismatches : activer event MPM tout en chargeant mod_php, ou définir un MaxRequestWorkers agressif sans marge mémoire.
WordPress devient le bouc émissaire pendant qu’Apache tue silencieusement des workers par OOM.

6) HTTP/2 (mod_http2) : excellent jusqu’à ce que proxies et en-têtes soient mauvais

HTTP/2 aide généralement les frontends WordPress en multiplexant les requêtes. Mais les mauvaises configurations causent des échecs étranges :

  • Certaines connexions clients bloquent à cause de proxies intermédiaires ou de réglages TLS bogués.
  • Augmentation subite du CPU avec certains suites de chiffrement et réglages de compression.
  • Push (si utilisé) peut rendre les caches grincheux et gaspiller de la bande passante. La plupart des sites ne devraient plus utiliser HTTP/2 server push.

7) Modules de compression : mod_deflate vs mod_brotli et le piège de l’en-tête Vary

La compression économise la bande passante. Elle peut aussi créer un empoisonnement de cache si votre couche de cache ne respecte pas la négociation de contenu.
Si vous activez brotli et gzip, vous devez avoir les bons en-têtes de réponse, en particulier Vary: Accept-Encoding.

Une erreur fréquente : activer brotli pour tout, puis découvrir qu’un cache en amont sert du CSS compressé en brotli à un client qui ne le supporte pas.
La page devient une exposition d’art moderne de styles cassés.

8) En-têtes de cache et mod_expires : « optimisation » qui fige votre thème dans le temps

Vous voulez de longues durées de cache pour les assets statiques. Vous ne voulez pas de longues durées pour le HTML ou pour des endpoints dynamiques.
Les thèmes et plugins WordPress livrent des assets qui changent ; si vous définissez un cache trop agressif sans fichiers versionnés, les utilisateurs conservent le vieux JS pour toujours.

9) mod_headers : en-têtes de sécurité qui cassent les flux de connexion

Les en-têtes de sécurité comptent. Mais vous pouvez casser l’authentification WordPress et les embeds si vous êtes trop zélé :

  • SameSite des cookies avec des réglages étranges peuvent casser des intégrations tierces.
  • Content-Security-Policy trop stricte casse l’éditeur par blocs, les embeds et l’analytics.
  • X-Frame-Options bloque des cas légitimes d’iframe (prévisualisations, certains flux SSO) si vous n’avez pas d’exceptions planifiées.

10) DirectoryIndex, MultiViews, et autres fonctionnalités « petites » qui causent de grandes bizarreries

Des fonctionnalités Apache qui semblent sans rapport avec WordPress entrent souvent en collision avec lui :

  • MultiViews (négociation de contenu) : peut casser les permaliens en mappant des URLs vers des fichiers inattendus. Si vous exécutez WordPress, vous voulez généralement le désactiver.
  • DirectoryIndex mal configuré : peut provoquer des 403/404 là où vous attendiez le front controller WordPress.
  • Collisions Alias et ProxyPass : un seul chemin en conflit peut mettre en panne /wp-admin ou /wp-json.

Blague #1 : Apache peut presque tout faire. C’est aussi le problème — vos collègues le peuvent aussi.

Tâches pratiques : commandes, sorties et décisions

Voici des tâches concrètes que vous pouvez exécuter sur un hôte Apache. Chacune indique ce que la sortie signifie et la décision à prendre.
Ce ne sont pas des « agréables à avoir ». Ce sont le chemin le plus court des symptômes à la cause.

Tâche 1 : Confirmer le mode réel de panne depuis l’origine avec curl

cr0x@server:~$ curl -svL -o /dev/null https://example.com/ 2>&1 | sed -n '1,25p'
*   Trying 203.0.113.10:443...
* Connected to example.com (203.0.113.10) port 443 (#0)
> GET / HTTP/2
> host: example.com
> user-agent: curl/7.81.0
> accept: */*
< HTTP/2 301
< location: http://example.com/
< server: Apache
* Issue another request to this URL: 'http://example.com/'
> GET / HTTP/1.1
> Host: example.com
< HTTP/1.1 301 Moved Permanently
< Location: https://example.com/

Ce que cela signifie : Vous avez une boucle de changement de schéma : HTTPS redirige vers HTTP, puis HTTP redirige vers HTTPS.
C’est habituellement deux sources de redirection différentes qui s’affrontent (réécriture Apache plus canonicalisation côté application).

Décision : Trouvez et supprimez un côté. Préférez : Apache applique HTTPS ; WordPress doit être configuré pour croire qu’il est derrière HTTPS (en-têtes proxy).

Tâche 2 : Voir quel MPM Apache est actif

cr0x@server:~$ apachectl -V | egrep -i 'server mpm|httpd_root|server_config_file'
Server MPM:     event
 -D HTTPD_ROOT="/etc/apache2"
 -D SERVER_CONFIG_FILE="apache2.conf"

Ce que cela signifie : Vous êtes sur event MPM. C’est bon pour la concurrence, mais vous ne devez pas utiliser mod_php.

Décision : Assurez-vous que PHP tourne via PHP-FPM (proxy_fcgi). Si vous voyez prefork, vérifiez la marge mémoire et les comptages de workers.

Tâche 3 : Lister les modules chargés (cherchez les suspects habituels)

cr0x@server:~$ apachectl -M | egrep -i 'rewrite|security2|http2|deflate|brotli|headers|proxy_fcgi|mpm'
 headers_module (shared)
 http2_module (shared)
 proxy_fcgi_module (shared)
 rewrite_module (shared)
 security2_module (shared)
 deflate_module (shared)
 mpm_event_module (shared)

Ce que cela signifie : rewrite et security2 sont actifs, plus HTTP/2 et compression. N’importe lequel peut expliquer une régression.

Décision : Si vous avez des 403 : concentrez-vous sur security2 et l’authz. Si vous avez des redirections : concentrez-vous sur rewrite et les en-têtes proxy. Si vous avez des blocages clients bizarres : inspectez http2/TLS.

Tâche 4 : Confirmer si .htaccess est pris en compte

cr0x@server:~$ apachectl -S 2>/dev/null | sed -n '1,40p'
VirtualHost configuration:
*:80                   example.com (/etc/apache2/sites-enabled/example.conf:1)
*:443                  example.com (/etc/apache2/sites-enabled/example-ssl.conf:1)
ServerRoot: "/etc/apache2"
Main DocumentRoot: "/var/www/html"

Ceci n’affiche pas AllowOverride directement, donc vérifiez le fichier vhost.

cr0x@server:~$ sudo apachectl -t -D DUMP_INCLUDES | sed -n '1,120p'
Included configuration files:
  (/etc/apache2/apache2.conf:215)
  (/etc/apache2/sites-enabled/example.conf:1)
  (/etc/apache2/sites-enabled/example-ssl.conf:1)

Maintenant inspectez la section directory :

cr0x@server:~$ sudo sed -n '1,140p' /etc/apache2/sites-enabled/example.conf
<VirtualHost *:80>
  ServerName example.com
  DocumentRoot /var/www/example.com/public

  <Directory /var/www/example.com/public>
    Options FollowSymLinks
    AllowOverride None
    Require all granted
  </Directory>
</VirtualHost>

Ce que cela signifie : .htaccess est ignoré. Les règles de réécriture WordPress dans .htaccess ne s’appliqueront pas. Les permaliens feront des 404 ou se comporteront étrangement.

Décision : Soit définissez AllowOverride FileInfo (ou All, si vous aimez les audits), soit déplacez les règles WordPress dans le vhost.

Tâche 5 : Valider que les règles de réécriture WordPress existent là où vous le pensez

cr0x@server:~$ sudo grep -n "BEGIN WordPress" -n /var/www/example.com/public/.htaccess
2:# BEGIN WordPress

Ce que cela signifie : Les règles existent dans .htaccess, mais la Tâche 4 montre qu’Apache les ignore.

Décision : Ne « re-enregistrez pas les permaliens » dans wp-admin en prétendant que cela a résolu le problème. Corrigez Apache d’abord.

Tâche 6 : Vérifier MultiViews (ça casse les « pretty URLs » de façon amusante)

cr0x@server:~$ sudo apachectl -t -D DUMP_RUN_CFG | egrep -i 'multiviews|options'

Ce que cela signifie : Ce dump n’est pas toujours verbeux sur Options au niveau répertoire.

Décision : Greppez votre config pour MultiViews et désactivez-le pour les docroots WordPress.

cr0x@server:~$ sudo grep -RIn "MultiViews" /etc/apache2 | head
/etc/apache2/conf-enabled/negotiation.conf:12:Options +MultiViews

Décision : Changez-le en Options -MultiViews dans le vhost WordPress ou le stanza Directory.

Tâche 7 : Confirmer le wiring du gestionnaire PHP (socket FPM et permissions)

cr0x@server:~$ sudo apachectl -t -D DUMP_MODULES | grep -E 'proxy_fcgi|php'
 proxy_fcgi_module (shared)
cr0x@server:~$ sudo grep -RIn "SetHandler.*proxy:unix" /etc/apache2/sites-enabled/example-ssl.conf
38:  <FilesMatch \.php$>
39:    SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost/"
40:  </FilesMatch>
cr0x@server:~$ ls -l /run/php/php8.2-fpm.sock
srw-rw---- 1 www-data www-data 0 Dec 27 09:05 /run/php/php8.2-fpm.sock

Ce que cela signifie : Le socket existe et appartient à www-data. Apache doit tourner en tant que www-data (ou être dans ce groupe) pour se connecter.

Décision : Si vous voyez « permission denied » dans les logs d’erreur Apache, corrigez la propriété/groupe du socket ou l’utilisateur Apache, pas WordPress.

Tâche 8 : Lire les erreurs Apache autour du moment de la panne (ne parcourez pas en diagonale)

cr0x@server:~$ sudo tail -n 60 /var/log/apache2/error.log
[Wed Dec 27 09:11:02.331221 2025] [security2:error] [pid 22190] [client 198.51.100.24:51422] [id "949110"] [msg "Inbound Anomaly Score Exceeded"] [uri "/wp-admin/admin-ajax.php"]
[Wed Dec 27 09:11:02.331339 2025] [security2:error] [pid 22190] [client 198.51.100.24:51422] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5'"...

Ce que cela signifie : mod_security a bloqué un endpoint AJAX WordPress. L’ID de règle est visible (949110 ici).

Décision : Créez une exception ciblée (supprimez ou modifiez cette règle pour ce vhost/chemin), et vérifiez que vous ne masquez pas une vraie attaque. Utilisez les logs d’audit pour valider les patterns.

Tâche 9 : Inspecter le journal d’audit ModSecurity pour le contexte complet

cr0x@server:~$ sudo grep -n "949110" /var/log/apache2/modsec_audit.log | tail -n 5
245817:Message: Warning. Pattern match "..." at ARGS:action. [file "/usr/share/modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "78"] [id "949110"]

Ce que cela signifie : Vous avez le fichier de règles CRS et le numéro de ligne. C’est exploitable.

Décision : Préférez des exclusions par ID de règle + emplacement restreint (par ex. seulement wp-admin/admin-ajax.php) plutôt que de désactiver globalement le CRS.

Tâche 10 : Prouver le comportement de réécriture avec du tracing type RewriteLog (méthode Apache 2.4)

Apache 2.4 n’utilise pas la directive RewriteLog ancienne. Activez le logging de trace par module :

cr0x@server:~$ sudo apachectl -t -D DUMP_VHOSTS | sed -n '1,40p'
VirtualHost configuration:
*:80                   example.com (/etc/apache2/sites-enabled/example.conf:1)
*:443                  example.com (/etc/apache2/sites-enabled/example-ssl.conf:1)

Ajoutez temporairement (pour un seul vhost) quelque chose comme :
LogLevel warn rewrite:trace3
puis reload et reproduisez. Ensuite inspectez les logs :

cr0x@server:~$ sudo tail -n 40 /var/log/apache2/error.log
[rewrite:trace3] [pid 22501] mod_rewrite.c(477): [client 198.51.100.24:52011]  applying pattern '^/(.*)$' to uri '/'
[rewrite:trace3] [pid 22501] mod_rewrite.c(477): [client 198.51.100.24:52011]  rewrite '/' -> '/index.php'

Ce que cela signifie : Vous pouvez voir quels patterns ont matché et comment l’URI a été réécrite.

Décision : Si une règle réécrit de façon inattendue vers une redirection, retirez-la ou contraignez-la. Si les règles WordPress ne s’exécutent jamais, votre contexte de répertoire/AllowOverride est faux.

Tâche 11 : Détecter le « mauvais schéma derrière un proxy » en vérifiant les en-têtes de requête et l’env Apache

cr0x@server:~$ curl -s -D - -o /dev/null https://example.com/wp-login.php | egrep -i 'location:|server:|set-cookie:'
server: Apache
location: http://example.com/wp-login.php?redirect_to=...

Ce que cela signifie : WordPress génère des redirections HTTP alors que le navigateur utilise HTTPS. Habituellement manque de prise en charge de X-Forwarded-Proto ou mauvais site URL WordPress.

Décision : Corrigez la propagation des en-têtes proxy et dites à Apache/WordPress quel est le schéma d’origine. Vérifiez aussi les réglages WordPress siteurl/home.

Tâche 12 : Vérifier KeepAlive et les timeouts (sites lents ou « qui échouent aléatoirement »)

cr0x@server:~$ sudo apachectl -t -D DUMP_RUN_CFG | egrep -i 'KeepAlive|Timeout|MaxKeepAliveRequests|KeepAliveTimeout'
KeepAlive: On
MaxKeepAliveRequests: 100
KeepAliveTimeout: 5
Timeout: 60

Ce que cela signifie : Valeurs par défaut raisonnables. Si KeepAliveTimeout est énorme, vous pouvez épuiser les workers sous charge.

Décision : Pour des sites WordPress très sollicités, gardez KeepAliveTimeout bas (généralement 2–5 secondes) et laissez HTTP/2 faire le multiplexing.

Tâche 13 : Vérifier la saturation des workers dans server-status Apache (si activé)

cr0x@server:~$ curl -s http://127.0.0.1/server-status?auto | egrep 'BusyWorkers|IdleWorkers|ReqPerSec|CPULoad'
CPULoad: .322
ReqPerSec: 18.2
BusyWorkers: 245
IdleWorkers: 0

Ce que cela signifie : Vous n’avez plus de workers idle. Les nouvelles connexions vont se mettre en file ou échouer. C’est un problème de capacité Apache, pas « WordPress lent ».

Décision : Augmentez MaxRequestWorkers seulement si vous avez de la marge mémoire. Sinon réduisez KeepAliveTimeout, corrigez les appels backend lents, et scalez horizontalement.

Tâche 14 : Vérifier la saturation PHP-FPM (max children reached)

cr0x@server:~$ sudo tail -n 40 /var/log/php8.2-fpm.log
[27-Dec-2025 09:22:14] WARNING: [pool www] server reached pm.max_children setting (40), consider raising it
[27-Dec-2025 09:22:20] NOTICE: [pool www] child 17732 started

Ce que cela signifie : FPM est saturé. Apache peut aller bien ; ce sont les workers PHP qui sont pleins.

Décision : Ne relevez pas bêtement pm.max_children. Mesurez la mémoire par processus PHP, confirmez la latence BD, et ajustez en fonction de la RAM et de la charge.

Tâche 15 : Confirmer que les assets statiques sont correctement mis en cache (et pas le HTML)

cr0x@server:~$ curl -sI https://example.com/wp-content/themes/site/style.css | egrep -i 'cache-control|expires|etag|vary|content-encoding'
cache-control: public, max-age=31536000
etag: "2c1b-5f3c1a4b"
vary: Accept-Encoding
content-encoding: br

Ce que cela signifie : Parfait pour des assets versionnés. Si votre thème utilise des noms de fichiers non versionnés, cela peut figer l’ancien CSS pour les utilisateurs.

Décision : Utilisez des URLs d’assets versionnées (query string ou noms de fichiers hachés) si vous définissez un long max-age. N’appliquez pas ce cache au HTML.

Tâche 16 : Détecter rapidement une erreur « tout mettre en cache » sur le HTML

cr0x@server:~$ curl -sI https://example.com/ | egrep -i 'cache-control|set-cookie|vary'
cache-control: public, max-age=31536000
set-cookie: wordpress_logged_in=...

Ce que cela signifie : Vous mettez en cache le HTML publiquement tout en émettant des cookies d’authentification. C’est comme saupoudrer la session d’un utilisateur sur la navigation d’un autre.

Décision : Corrigez la politique de cache : le HTML doit généralement être private ou no-store pour les chemins authentifiés, et soigneusement contrôlé pour le trafic anonyme.

Trois micro-récits du monde de l’entreprise

Micro-récit 1 : L’incident provoqué par une mauvaise hypothèse

Une entreprise de taille moyenne a migré un site marketing WordPress derrière un load balancer qui terminait le TLS.
L’origine Apache ne parlait qu’en HTTP en interne, ce qui est normal et acceptable — si vous apprenez à l’application quelle est la réalité.
Quelqu’un a dit : « WordPress s’en chargera. » Cette phrase devrait être bannie des chats de prod.

En quelques minutes après la bascule, les utilisateurs mobiles se sont coincés dans une boucle de redirection. Les utilisateurs desktop passaient parfois.
Les traces HTTP montraient un pattern : les requêtes arrivaient au load balancer en HTTPS, étaient envoyées à Apache en HTTP,
et WordPress générait des redirections vers HTTP parce qu’il croyait tourner en HTTP simple.

L’équipe a d’abord poursuivi les règles de réécriture. Puis ils ont cherché HSTS. Puis le CDN.
La percée est venue du travail ennuyeux : capturer une chaîne requête/réponse complète depuis l’origine avec les en-têtes,
puis vérifier quels en-têtes le load balancer injectait réellement.

L’origine ne recevait pas X-Forwarded-Proto: https de façon cohérente parce qu’un listener manquait la règle d’injection d’en-tête.
WordPress voyait des signaux mixtes et essayait de « corriger » les URLs dans les deux sens. La boucle n’était pas mystérieuse ; elle était déterministe.

Correction : rendre les en-têtes proxy cohérents, configurer Apache pour faire confiance au proxy, et configurer WordPress pour considérer le proto forwardé comme faisant foi.
Puis simplifier les réécritures Apache : une redirection canonique, une seule. La panne a pris fin. Le postmortem a ajouté une règle :
aucune migration d’infra sans une transcription curl dans le ticket.

Micro-récit 2 : L’optimisation qui a mal tourné

Une autre organisation a décidé « d’améliorer les performances » en désactivant le scan .htaccess.
L’idée était juste : AllowOverride None réduit les lookups système et peut éliminer une classe de dérive de config.
L’exécution était le problème : ils ont basculé le réglage un vendredi après-midi sans déplacer les règles de réécriture dans la config vhost.

Le site n’est pas tombé complètement. Il a fait pire : il a majoritairement fonctionné, sauf pour tout ce qui fait d’un site un site.
La page d’accueil chargeait, mais les articles faisaient 404. Les pages de catégories faisaient 404. Les endpoints JSON utilisés par l’éditeur par blocs échouaient.
Les tickets support décrivaient cela comme « aléatoire ». Rien n’est aléatoire ; c’était du routage basé sur le chemin qui s’effondrait.

Ils ont rollback et appelé ça un « problème Apache ». Pas tout à fait. C’était un problème de gestion du changement.
Désactiver AllowOverride est une migration. Traitez-la comme une migration : reproduisez les règles, testez les permaliens, testez wp-admin, testez wp-json,
et seulement alors retirez le support .htaccess.

Le résultat long terme a été positif : ils ont finalement déplacé les règles dans le vhost, ajouté des tests d’intégration qui curlent un jeu d’URLs représentatives,
et retiré beaucoup de fragments de réécriture de plugins obsolètes de .htaccess. Les performances ont augmenté et la config est devenue plus saine.
Mais la leçon était simple : les optimisations ne valent rien si elles suppriment des fonctionnalités.

Micro-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la journée

Une grande entreprise avait une flotte WordPress avec une baseline Apache commune : modules standards, en-têtes standards, politique TLS standard.
Chaque site avait aussi ses propres plugins et équipes éditoriales, ce qui est une manière polie de dire « payloads de requête imprévisibles ».
Ils faisaient tourner mod_security avec le CRS, parce que la conformité l’exigeait.

Chaque fois qu’une mise à jour de plugin déclenchait des faux positifs, d’autres équipes voulaient désactiver le WAF « temporairement ».
L’équipe plateforme a refusé. À la place ils avaient une procédure : collecter l’événement d’audit mod_security, identifier l’ID de règle,
confirmer que la charge utile est légitime, écrire une exclusion étroite pour le site et le endpoint, et l’attacher à une demande de changement.
C’était fastidieux. C’était aussi reproductible.

Un lundi matin, une vague de requêtes a commencé à toucher /wp-login.php avec des patterns de paramètres étranges.
mod_security a bloqué la plupart. Une poignée d’actions d’administration légitimes ont aussi été bloquées, et les éditeurs ont râlé vite.
L’équipe a utilisé le workflow établi : ils ont affiné une règle pour un appel admin-ajax spécifique tout en gardant les protections plus larges.

Le site est resté up, l’authentification est restée protégée, et le seul vrai « dommage » a été une réunion de 20 minutes pour approuver une exception de règle.
Personne n’a écrit d’histoire héroïque. C’est le but. La pratique ennuyeuse bat les incidents excitants.

Erreurs courantes : symptôme → cause racine → correctif

Boucle de redirection (ERR_TOO_MANY_REDIRECTS)

Symptôme : Le navigateur boucle entre HTTP et HTTPS, ou entre www et non-www.

Cause racine : Canonicalisation contradictoire entre la réécriture Apache, les réglages WordPress, et le comportement proxy/CDN.

Correctif : Choisissez une couche pour imposer l’hôte/schéma canonique (généralement edge ou Apache). Assurez la cohérence de X-Forwarded-Proto et X-Forwarded-Host, et que siteurl/home de WordPress correspondent à l’URL publique.

Permaliens 404, mais la page d’accueil fonctionne

Symptôme : / charge, /2025/… fait 404, /wp-json échoue.

Cause racine : .htaccess ignoré à cause de AllowOverride None, ou règles de réécriture manquantes dans la config vhost.

Correctif : Activez AllowOverride FileInfo pour le répertoire docroot ou migrez les règles de réécriture WordPress dans le vhost.

403 Forbidden sur wp-admin ou admin-ajax

Symptôme : Pages d’administration qui chargent partiellement ; appels AJAX qui échouent ; l’éditeur affiche des erreurs ; logs montrent des 403.

Cause racine : Faux-positif mod_security ou mauvaise configuration d’authz Apache (Require rules, méthodes bloquées, etc.).

Correctif : Vérifiez le log d’erreur Apache pour les IDs de règle ; affinez les exceptions mod_security de façon étroite. Si c’est de l’authz, corrigez les permissions de répertoire et mettez Require all granted là où c’est approprié.

502 Bad Gateway après une « petite » mise à jour PHP

Symptôme : Apache retourne 502 pour toutes les pages PHP ; les fichiers statiques continuent d’être servis.

Cause racine : proxy_fcgi Apache pointe encore vers un ancien chemin de socket PHP-FPM, ou le service FPM n’est pas en cours.

Correctif : Validez le chemin de socket dans SetHandler et l’état du service ; reload Apache après mise à jour. Confirmez les permissions du socket.

Site lent, CPU correct mais utilisateurs se plaignent

Symptôme : Haute latence, timeouts occasionnels, pas de pic CPU évident.

Cause racine : Épuisement des workers dû à un KeepAliveTimeout long, saturation du backend (FPM max children), ou waits IO disque pour sessions PHP/uploads.

Correctif : Inspectez BusyWorkers/IdleWorkers, les logs FPM pour max_children, et réduisez KeepAliveTimeout. Ensuite profilez les endpoints lents.

Changements de thème/JS qui n’apparaissent pas pour les utilisateurs

Symptôme : « J’ai vidé mon cache » devient la routine cardio du support.

Cause racine : Cache-Control/Expires agressif pour des assets non versionnés.

Correctif : Versionnez les assets (noms hachés ou query string). Gardez un long cache seulement pour les assets versionnés ; cache plus court pour les fichiers dynamiques ou fréquemment mis à jour.

Erreurs REST API, éditeur par blocs cassé, mais frontend OK

Symptôme : L’éditeur ne peut pas sauvegarder, affiche « Publishing failed », ou les embeds échouent.

Cause racine : mod_security bloque les payloads JSON, les règles de réécriture ne routent pas /wp-json, ou en-têtes/CORS mal configurés.

Correctif : Confirmez que /wp-json/ renvoie 200 depuis l’origine, affinez mod_security, et assurez-vous que les règles front controller s’appliquent à ce chemin.

Blague #2 : La phrase la plus dangereuse en ops est « c’est juste un changement d’en-tête ».

Listes de contrôle / plan étape par étape

Checklist A : Stabiliser un site WordPress cassé sous Apache (30–60 minutes)

  1. Capturer une requête qui échoue avec curl (-svL) et sauvegarder la sortie dans le ticket d’incident.
  2. Corréler l’horodatage avec le log d’erreur Apache et le log PHP-FPM.
  3. Classer en redirect/403/5xx/lent et choisir l’ensemble de modules à inspecter.
  4. Confirmer si .htaccess est honoré (AllowOverride) et si les règles de réécriture existent où attendu.
  5. Vérifier les hits mod_security par ID de règle ; ne devinez pas.
  6. Valider le chemin du socket PHP-FPM et les permissions ; confirmer que le service est sain.
  7. Mesurer la saturation des workers (Apache BusyWorkers/IdleWorkers ; avertissements FPM max_children).
  8. Faire un changement qui supprime le mode de panne principal (ex. : retirer la redirection conflictuelle, ajouter une exception WAF étroite, corriger le chemin du socket).
  9. Plan de rollback : assurez-vous de pouvoir revenir rapidement la config (fichier vhost précédent, état module connu-bon).
  10. Re-tester : page d’accueil, un permalien, wp-admin login, admin-ajax, wp-json.

Checklist B : Prévenir la prochaine panne (la partie que personne n’a planifiée)

  1. Standardiser la canonicalisation : décidez où résident les redirections (edge vs Apache vs app). Documentez-le.
  2. Arrêter de dupliquer les règles à travers Apache et WordPress. Une chaîne de redirections canonique doit exister, pas trois.
  3. Déplacer la config de réécriture dans le vhost si vous le pouvez, et garder AllowOverride None pour la performance et la déterminisme.
  4. Journaliser ce dont vous avez besoin : temps de requête, temps upstream, statut, user agent, host, X-Forwarded-Proto.
  5. Conserver mod_security, mais gérez-le : rétention des logs d’audit, exceptions par ID de règle, et un processus d’approbation simple.
  6. Tester la charge avec réalisme : incluez wp-admin et wp-json, pas seulement des hits anonymes sur la page d’accueil.
  7. Limiter le rayon d’impact : configs par vhost, rollouts en staging, et feature flags pour les modules risqués (HTTP/2, brotli) si possible.

Checklist C : Processus de changement Apache sûr pour WordPress

  1. Différez la config dans une PR (ou au moins une demande de changement) et exigez une seconde paire d’yeux.
  2. Exécutez apachectl -t avant reload.
  3. Déployez sur un vhost/host canary et exécutez une suite de tests curl scriptée (front page, permaliens, wp-login, wp-admin, wp-json).
  4. Surveillez les logs d’erreur et les taux 4xx/5xx pendant 15–30 minutes.
  5. Déployez progressivement. Si vos outils ne supportent pas les rollouts progressifs, vos outils sont le risque.

FAQ

1) Dois-je utiliser .htaccess pour WordPress sur Apache ?

Si vous êtes en hébergement mutualisé, vous y êtes probablement obligé. Si vous opérez le serveur, préférez la config vhost.
.htaccess coûte en performance et crée une « config invisible » que votre processus de déploiement ne versionne pas bien.

2) Quel est le moyen le plus rapide de savoir si les règles de réécriture posent problème ?

Curl un permalien connu et regardez le code de statut. Puis vérifiez si Apache honore .htaccess (AllowOverride).
Si les permaliens font 404 et AllowOverride est None, vous avez la réponse : Apache ignore les règles.

3) Pourquoi wp-admin fonctionne mais l’éditeur par blocs échoue ?

L’éditeur par blocs repose fortement sur les endpoints REST API sous /wp-json et sur des appels AJAX.
mod_security, des réécritures cassées, ou des méthodes requêtes bloquées peuvent casser sélectivement ces endpoints tout en laissant les pages HTML intactes.

4) Est-ce que mod_security vaut la peine pour WordPress ?

Oui, si vous l’opérez comme un adulte : logs d’audit, exceptions ciblées, et revue périodique.
Non, si votre mode opératoire unique est « le désactiver quand il s’énerve ».

5) prefork vs event MPM : que choisir pour WordPress ?

Si vous utilisez PHP-FPM, utilisez event MPM dans la plupart des cas. Si vous utilisez mod_php (non recommandé pour les setups modernes), vous êtes coincé avec prefork.
La bonne réponse est généralement « event + PHP-FPM », puis ajustez les workers selon la mémoire et le trafic.

6) Est-ce que HTTP/2 peut casser mon site WordPress ?

En général non, mais cela peut exposer des mauvaises configurations : particularités TLS, proxies bogués, ou comportement de cache incorrect.
Si l’activation de HTTP/2 corrèle avec des blocages clients ou des chargements partiels bizarres, testez en désactivant HTTP/2 temporairement sur un vhost et comparez.

7) Pourquoi j’obtiens un 403 seulement sur admin-ajax.php ?

admin-ajax transporte souvent des payloads qui ressemblent à des attaques pour des règles WAF génériques.
Confirmez dans le log d’erreur Apache ; vous verrez souvent un ID de règle mod_security. Excluez de manière étroite pour cet endpoint et cet ID de règle.

8) Mon CSS/JS est mis en cache pour toujours après activation de mod_expires. Comment corriger sans désactiver le cache ?

Gardez de longues durées de cache pour les assets versionnés seulement. Si les noms de fichiers ne sont pas versionnés, ajoutez des versions (hashes ou query strings) dans votre build/deploy de thème.
Raccourcissez le cache pour les assets non versionnés. Le caching n’est pas l’ennemi ; les assets non versionnés le sont.

9) WordPress continue de rediriger vers HTTP alors que mon site est en HTTPS. Est-ce Apache ou WordPress ?

C’est presque toujours un « mismatch de réalité proxy ». Apache voit de l’HTTP depuis le load balancer et transmet cette impression à PHP.
Corrigez la gestion du forwarded proto/host et les réglages d’URL WordPress, puis enlevez les redirections dupliquées.

10) Quel est l’ensemble minimal sûr de modules Apache pour WordPress ?

Typiquement : rewrite, headers, modules TLS, un handler PHP (proxy_fcgi), et optionnellement deflate ou brotli.
Ajoutez http2 si votre environnement le supporte proprement. Ajoutez mod_security si vous pouvez l’exploiter correctement.

Conclusion : que faire ensuite (et arrêter de faire)

WordPress est souvent blâmé pour beaucoup de problèmes Apache parce que c’est ce que les utilisateurs voient.
En pratique, les récidivistes sont des couches de configuration : règles de réécriture qui se combattent, .htaccess ignoré,
règles WAF qui bloquent du trafic légitime, et des « améliorations de performance » non testées contre de vrais endpoints.

Prochaines étapes qui rapportent tout de suite :

  • Écrivez une politique de canonicalisation (hôte + schéma) et appliquez-la en un seul endroit.
  • Décidez si vous êtes un shop .htaccess ou un shop config vhost. Choisissez-en un. Appliquez-le de façon cohérente.
  • Activez les logs nécessaires pour dépanner vite (incluant le temps de requête), et faites de la « transcription curl » une hygiène d’incident.
  • Traitez les exceptions mod_security comme du code : étroites, revues, et justifiées.
  • Quand vous ajustez Apache, faites-le avec des mesures de capacité (BusyWorkers/saturation FPM), pas à l’instinct.

Arrêtez de faire ceci :

  • Arrêtez d’empiler des redirections sur trois couches et d’être surpris quand elles forment une bande de Möbius.
  • Arrêtez de désactiver globalement les contrôles de sécurité parce qu’une mise à jour de plugin a chauffé.
  • Arrêtez d’envoyer les mêmes en-têtes de cache pour le HTML et pour les images.

L’objectif n’est pas une configuration Apache brillante. L’objectif est un site WordPress qui reste en ligne, reste rapide, et échoue de façon diagnostiquable avant le déjeuner.

← Précédent
MySQL vs PostgreSQL : mémoire Docker — arrêter l’étouffement silencieux
Suivant →
Tests CPU en conditions réelles : une méthode simple pour votre charge de travail

Laisser un commentaire