Le .htaccess WordPress a cassé le site : restaurer un défaut sûr correctement

Cet article vous a aidé ?

Vous avez modifié .htaccess pour « juste ajouter une redirection » ou « renforcer la sécurité », rafraîchi la page, et maintenant la production renvoie des 500, 403 ou une boucle de redirection qui fait chauffer le CPU comme un radiateur. Le téléphone se met à sonner. L’équipe marketing dit que ce n’est « que le blog ». Pendant ce temps, c’est aussi votre panier, votre documentation, votre SEO et votre week-end.

Voici la sortie raisonnable : restaurer une base connue bonne, prouver ce qui a cassé, et mettre des garde-fous afin que la prochaine modification ne devienne pas un incident. On va le faire comme un SRE qui a dû expliquer à la direction pourquoi « un petit ajustement de config » a pris en défaut un chemin de revenu.

Ce que fait réellement .htaccess (et pourquoi ça casse si violemment)

.htaccess est une configuration par répertoire pour Apache HTTP Server. Si votre configuration Apache l’autorise (AllowOverride), Apache lit .htaccess à chaque requête pour ce répertoire et ses sous-répertoires. C’est le but : vous pouvez faire des changements sans toucher à la configuration globale ni recharger Apache. C’est aussi le piège : vous pouvez casser instantanément le routage, les permissions et la sécurité, et Apache appliquera fidèlement la logique cassée à chaque requête.

WordPress s’appuie sur mod_rewrite pour faire fonctionner les « permaliens jolis ». Le bloc de réécriture par défaut de WordPress est petit et ennuyeux. Ce qui est exactement ce que vous voulez en production. Quand un plugin, un guide de « durcissement » de sécurité, ou un collègue bien intentionné commence à empiler des directives — redirections, règles deny, flags PHP, indices de cache — vous pouvez créer des interactions qui ne sont pas évidentes avant la mise en production : boucles de réécriture, amplification de requêtes, assets bloqués, admin bloqué, et des 500 soudains.

Une chose de plus : .htaccess est évalué dans le contexte d’un répertoire, pas de tout le site. Cela signifie qu’une règle qui « semble correcte » à la racine peut se comporter différemment dans /wp-admin/ ou /wp-content/. Les règles de correspondance de chemin sont subtiles. Les incidents en production ne le sont pas.

Blague courte #1 : Éditer .htaccess en direct, c’est comme faire de la chirurgie avec un couteau à beurre — techniquement possible, mais vous aurez beaucoup d’explications à donner ensuite.

Faits et contexte intéressants (pour comprendre le comportement)

  • .htaccess existe à cause de l’hébergement mutualisé. Aux débuts de l’hébergement bon marché, les utilisateurs avaient besoin d’un contrôle limité sans accès root. Les overrides par répertoire étaient le compromis.
  • Apache lit .htaccess au moment de la requête. Cette commodité a un coût en performance ; le serveur doit chercher le fichier (et les répertoires parents) à répétition sauf configuration contraire.
  • Les permaliens WordPress sont devenus « l’attente par défaut ». Au fur et à mesure que les blogs sont devenus des CMS, les URLs lisibles par l’humain ont cessé d’être optionnelles et sont devenues la norme.
  • L’ordre des règles de mod_rewrite est un piège courant. La première règle correspondante peut court-circuiter le reste, et un petit changement de flag (L, R, QSA) peut inverser complètement le comportement.
  • 403 vs 404 est souvent une question de politique, pas d’existence. Un 403 d’Apache peut être causé par les permissions du système de fichiers, des règles d’accès, ou des modules de sécurité — pas forcément un contenu manquant.
  • Beaucoup de « snippets de sécurité » sont obsolètes. Des billets de blog copiés pendant des années recommandent encore des directives qui entrent en conflit avec le comportement moderne de WordPress ou des configurations PHP-FPM.
  • Nginx n’utilise pas .htaccess. Lorsque des équipes migrent d’Apache vers Nginx, elles continuent souvent d’éditer .htaccess et se demandent pourquoi rien ne change.
  • AllowOverride est une décision du plan de contrôle. L’activer partout est pratique opérationnellement et hostile pour la sécurité ; le désactiver partout est sûr et pénible. La plupart des systèmes réels choisissent un compromis.

Procédure de diagnostic rapide

Voici l’ordre qui trouve le goulot rapidement, pas l’ordre qui paraît poli.

1) Identifier la classe d’échec : 500, 403, 404, boucle de redirection ou page blanche

  • 500 : Apache n’a pas pu traiter la requête. Penser directive invalide, module manquant, problème de permission sous un module de sécurité, ou échecs du handler PHP exposés par la réécriture.
  • 403 : Accès refusé. Penser permissions système de fichiers, règles Require/Deny, ou WAF/module de sécurité.
  • 404 : Routage. Penser réécriture qui ne se déclenche pas, mauvais RewriteBase, mauvais DocumentRoot, ou WordPress qui ne reçoit pas la requête.
  • 301/302 boucle : Conflit de logique de réécriture ou de redirection. Souvent canonicalisation scheme/host + plugin + en-têtes de proxy.
  • Page blanche : Peut être un fatal PHP avec affichage des erreurs désactivé ; peut aussi être déclenché par une réécriture vers PHP.

2) Regarder le journal d’erreurs du serveur web en premier, pas WordPress

Si .htaccess est cassé, Apache vous dira souvent exactement quelle directive et pourquoi. Si vous commencez par WordPress, vous déboguez la mauvaise couche.

3) Confirmer qu’Apache prend bien en charge .htaccess

Si AllowOverride None est défini pour ce chemin, vos changements ne s’appliqueront pas, et vous pourriez courir après des fantômes. À l’inverse, si les overrides sont autorisés, une seule ligne mauvaise peut faire tomber le vhost immédiatement.

4) Rétablir une base connue bonne, puis réintroduire les changements

En production, vous voulez une restauration rapide du service. La cause racine peut venir après. Faites-le proprement : sauvegardez le fichier courant, restaurez le défaut, validez les logs, puis ajoutez les changements un par un.

Restaurer un .htaccess WordPress sûr (correctement)

Il y a deux « défauts sûrs » dont il faut parler :

  1. Le bloc de réécriture par défaut de WordPress (le minimum requis pour les permaliens sur Apache avec mod_rewrite).
  2. Une base opérationnelle sûre : réécriture par défaut plus un petit ensemble de règles de sécurité non controversées qui ne cassent pas l’admin, l’API REST ou les uploads.

Baseline #1: Bloc de réécriture par défaut WordPress

C’est ce que WordPress écrit quand vous « Enregistrer les permaliens » dans l’admin. C’est volontairement minimal.

cr0x@server:~$ cat /var/www/example.com/public_html/.htaccess
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

Oui, cette ligne HTTP_AUTHORIZATION compte pour certains setups et plugins. Si vous la supprimez, vous pouvez casser des requêtes authentifiées via certains proxies ou handlers PHP. Gardez-la sauf raison contraire.

Baseline #2: Réécriture par défaut plus durcissement opérationnel sûr

Le durcissement nécessite de la retenue. Si vous ne pouvez pas expliquer exactement quelles requêtes il bloque et pourquoi, ça n’a pas sa place en production. Un ensemble conservateur inclut : empêcher l’indexation de répertoires, bloquer l’accès aux fichiers sensibles, et ajouter quelques en-têtes qui ne cassent pas WordPress.

Conservez le comportement de réécriture identique, et ajoutez seulement des règles à faible risque. Évitez les blocs regex sophistiqués sur les chaînes de requête tant que vous ne les avez pas testés avec vos plugins et workflows d’admin.

cr0x@server:~$ cat /var/www/example.com/public_html/.htaccess
# Basic hygiene
Options -Indexes

<FilesMatch "^(\.env|composer\.(json|lock)|wp-config\.php|readme\.html|license\.txt)$">
  Require all denied
</FilesMatch>

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

# Safe headers (avoid breaking admin)
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>

Remarquez ce qui manque : directives de cache agressives, blocage aveugle de xmlrpc.php, règles compliquées de hotlink, et « deny all in wp-admin except my IP ». Ce ne sont pas des défauts. Ce sont des décisions de projet.

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

Vous voulez des tâches répétables qui fonctionnent sous pression. En voici celles que j’exécute réellement. Chacune inclut (a) une commande, (b) ce que signifie la sortie, et (c) la décision que vous en tirez.

Task 1: Confirmer le serveur web et sa config active

cr0x@server:~$ ps -eo pid,comm,args | egrep 'apache2|httpd|nginx' | head
  1123 apache2 /usr/sbin/apache2 -k start
  1450 apache2 /usr/sbin/apache2 -k start

Signification : Apache tourne (apache2). Si vous ne voyez que nginx, cessez d’éditer .htaccess ; il n’est pas sur le chemin de requête.

Décision : Si Apache n’est pas la couche de service, localisez le véritable ingress (load balancer, reverse proxy, Nginx) et corrigez le routage là-bas.

Task 2: Capturer le symptôme HTTP exact depuis le serveur lui-même

cr0x@server:~$ curl -sS -D- -o /dev/null http://127.0.0.1/ | sed -n '1,12p'
HTTP/1.1 500 Internal Server Error
Date: Sat, 27 Dec 2025 12:10:13 GMT
Server: Apache/2.4.57 (Ubuntu)
Content-Type: text/html; charset=iso-8859-1

Signification : C’est un vrai 500 côté serveur web, pas un problème au niveau CDN.

Décision : Allez directement aux logs d’erreur Apache et à la validation de config avant de toucher WordPress.

Task 3: Tail des logs d’erreur Apache pendant une requête

cr0x@server:~$ sudo tail -n 50 /var/log/apache2/error.log
[Sat Dec 27 12:10:13.492112 2025] [core:alert] [pid 1123] [client 127.0.0.1:39912] /var/www/example.com/public_html/.htaccess: Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration

Signification : mod_rewrite n’est pas chargé, ou Apache ne connaît pas les directives de réécriture dans ce contexte.

Décision : Activer mod_rewrite (sur Debian/Ubuntu) et recharger Apache, ou corriger l’image/container/ensemble de paquets.

Task 4: Valider la disponibilité des modules Apache (mod_rewrite, mod_headers)

cr0x@server:~$ apache2ctl -M | egrep 'rewrite|headers'
 headers_module (shared)

Signification : headers est présent ; rewrite ne l’est pas. Cela correspond à l’erreur du log.

Décision : Activer rewrite, puis retester. Sans lui, les permaliens WordPress ne fonctionneront pas comme attendu.

Task 5: Activer mod_rewrite et recharger Apache (Debian/Ubuntu)

cr0x@server:~$ sudo a2enmod rewrite
Enabling module rewrite.
To activate the new configuration, you need to run:
  systemctl restart apache2

Signification : Le module est maintenant activé dans la config d’Apache.

Décision : Redémarrer/recharger Apache de façon contrôlée. Si vous êtes en HA, vider d’abord un nœud.

cr0x@server:~$ sudo systemctl reload apache2

Signification : Le reload a réussi (aucune sortie est courante). S’il échoue, systemctl status apache2 vous l’indiquera.

Décision : Relancez immédiatement curl et tail des logs pour confirmer que la classe d’erreur a changé.

Task 6: Vérifier si Apache est autorisé à prendre en compte .htaccess (AllowOverride)

cr0x@server:~$ sudo apache2ctl -S 2>&1 | sed -n '1,20p'
VirtualHost configuration:
*:80                   example.com (/etc/apache2/sites-enabled/example.com.conf:1)
ServerRoot: "/etc/apache2"
Main DocumentRoot: "/var/www/html"

Signification : Vous avez le chemin du fichier vhost. Inspectez ensuite le bloc de répertoire pour AllowOverride.

Décision : Si AllowOverride None, alors les règles .htaccess sont ignorées ; corrigez au niveau du vhost ou changez AllowOverride pour ce répertoire seulement.

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

  <Directory /var/www/example.com/public_html>
    AllowOverride All
    Require all granted
  </Directory>
</VirtualHost>

Signification : Les overrides sont autorisés. Donc .htaccess est en jeu, pour le meilleur ou pour le pire.

Décision : Si le site est cassé après une modification, restaurer .htaccess aura un impact immédiat.

Task 7: Faire une sauvegarde locale horodatée avant de changer quoi que ce soit

cr0x@server:~$ cd /var/www/example.com/public_html
cr0x@server:~$ sudo cp -a .htaccess .htaccess.bak.$(date +%F-%H%M%S)
cr0x@server:~$ sudo ls -la .htaccess*
-rw-r--r-- 1 www-data www-data  821 Dec 27 12:11 .htaccess
-rw-r--r-- 1 www-data www-data  821 Dec 27 12:11 .htaccess.bak.2025-12-27-121102

Signification : Vous avez maintenant une issue de secours. Si vous vous êtes trompé (ça arrive), vous pouvez restaurer rapidement.

Décision : N’éditez jamais sans sauvegarde. En réponse à incident, « je me souviendrai de ce que c’était » est un mensonge que vous vous racontez.

Task 8: Valider la propriété et les permissions du système de fichiers (les 403 démarrent souvent ici)

cr0x@server:~$ namei -l /var/www/example.com/public_html/.htaccess
f: /var/www/example.com/public_html/.htaccess
drwxr-xr-x root     root     /
drwxr-xr-x root     root     var
drwxr-xr-x root     root     www
drwxr-x--- www-data www-data example.com
drwxr-x--- www-data www-data public_html
-rw-r----- www-data www-data .htaccess

Signification : Apache (souvent www-data) peut lire le fichier si il tourne en tant que www-data. Si un répertoire manque le bit d’exécution pour l’utilisateur Apache, Apache ne peut pas traverser et vous obtiendrez des 403/500 étranges.

Décision : Si les permissions sont trop strictes, corrigez d’abord les bits d’exécution des répertoires avant d’accuser les règles de réécriture.

Task 9: Chercher la boucle de redirection classique avec les en-têtes

cr0x@server:~$ curl -sS -I http://example.com/ | sed -n '1,12p'
HTTP/1.1 301 Moved Permanently
Location: https://example.com/

Signification : HTTP redirige vers HTTPS. OK.

Décision : Testez maintenant HTTPS et vérifiez s’il y a une deuxième redirection vers HTTP, ou vers un autre hôte.

cr0x@server:~$ curl -sS -I https://example.com/ | egrep 'HTTP/|Location'
HTTP/2 301
location: http://example.com/

Signification : C’est une boucle : HTTPS redirige vers HTTP. Habituellement causé par des en-têtes proxy mixtes et des conditions de réécriture qui mal-détectent le scheme.

Décision : Corriger la canonicalisation en un seul endroit (LB ou Apache), et s’assurer que X-Forwarded-Proto est correctement pris en compte. Ne superposez pas des redirections concurrentes dans .htaccess et les plugins.

Task 10: Désactiver rapidement .htaccess sans le supprimer (test sûr)

cr0x@server:~$ cd /var/www/example.com/public_html
cr0x@server:~$ sudo mv .htaccess .htaccess.disabled
cr0x@server:~$ curl -sS -D- -o /dev/null http://127.0.0.1/ | sed -n '1,8p'
HTTP/1.1 200 OK
Date: Sat, 27 Dec 2025 12:12:44 GMT
Server: Apache/2.4.57 (Ubuntu)

Signification : Retirer .htaccess du chemin de requête a rétabli le service. C’est une confirmation, pas une solution.

Décision : Remettez un .htaccess minimal connu bon (bloc WordPress par défaut), puis réactivez le fichier.

Task 11: Restaurer proprement le bloc de réécriture par défaut WordPress

cr0x@server:~$ sudo tee /var/www/example.com/public_html/.htaccess >/dev/null <<'EOF'
# BEGIN WordPress

RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

# END WordPress
EOF
cr0x@server:~$ curl -sS -D- -o /dev/null http://127.0.0.1/ | sed -n '1,8p'
HTTP/1.1 200 OK
Date: Sat, 27 Dec 2025 12:13:21 GMT
Server: Apache/2.4.57 (Ubuntu)

Signification : La racine répond de nouveau. Confirmez maintenant les permaliens et l’admin.

Décision : Si cela corrige le problème, vous savez que les changements précédents dans .htaccess étaient le déclencheur. Réintroduisez ensuite les règles nécessaires avec précaution.

Task 12: Vérifier que le routage des permaliens fonctionne (un fichier inexistant doit atteindre index.php)

cr0x@server:~$ curl -sS -D- -o /dev/null http://127.0.0.1/this-should-not-be-a-file | sed -n '1,10p'
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8

Signification : La requête est routée via WordPress plutôt que de renvoyer un 404 d’Apache. WordPress peut toujours produire une page 404, mais c’est au niveau application.

Décision : Si vous obtenez un 404 d’Apache à la place, la réécriture ne fonctionne pas. Revérifiez mod_rewrite, AllowOverride et RewriteBase.

Task 13: Confirmer que WordPress voit le site URL et home URL corrects (les boucles de redirection vivent souvent ici)

cr0x@server:~$ sudo -u www-data wp option get home --path=/var/www/example.com/public_html
https://example.com
cr0x@server:~$ sudo -u www-data wp option get siteurl --path=/var/www/example.com/public_html
https://example.com

Signification : WordPress est d’accord sur le schéma et l’hôte canoniques.

Décision : Si ceux-ci diffèrent (http vs https, www vs apex), corrigez-les avant d’ajouter des redirections dans .htaccess. Laissez une seule couche gérer la canonicalisation.

Task 14: Vérifier que le handler PHP est sain (les 500 peuvent être imputés à tort à .htaccess)

cr0x@server:~$ curl -sS -D- -o /dev/null http://127.0.0.1/wp-login.php | sed -n '1,12p'
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8

Signification : L’exécution PHP fonctionne au moins pour ce point d’entrée.

Décision : Si ceci renvoie 500 alors que les assets statiques sont 200, le problème vient probablement de PHP-FPM, des permissions ou d’erreurs applicatives plutôt que de la réécriture elle-même.

Task 15: Valider la santé de la configuration Apache après des changements (attraper le « ne démarre pas »)

cr0x@server:~$ sudo apache2ctl configtest
Syntax OK

Signification : La configuration globale d’Apache se parse. Cela ne valide pas complètement chaque cas limite runtimes de .htaccess, mais ça attrape beaucoup de désastres.

Décision : Si ce n’est pas OK, corrigez avant de recharger. Un reload cassé sur une configuration mono-nœud est une panne auto-infligée.

Task 16: Trouver exactement quelle directive .htaccess casse les requêtes

cr0x@server:~$ sudo grep -RIn --color=never "Invalid command\|RewriteCond\|RewriteRule\|Require\|Deny" /var/log/apache2/error.log | tail -n 5
/var/log/apache2/error.log:[Sat Dec 27 12:10:13.492112 2025] [core:alert] [pid 1123] [client 127.0.0.1:39912] /var/www/example.com/public_html/.htaccess: Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration

Signification : Le log pointe vers le fichier et la directive en faute.

Décision : Corrigez le mismatch module/config plutôt que de supprimer des lignes au hasard.

Trois mini-récits d’entreprise depuis le terrain

Mini-récit 1 : La panne causée par une mauvaise hypothèse

L’entreprise était en pleine refonte de marque. Nouveau domaine, nouveaux certificats TLS, nouvelles pages de campagne. Un développeur a ajouté ce qui semblait être une redirection inoffensive dans .htaccess pour forcer tout vers le nouveau nom d’hôte. Il l’a testée sur son laptop. Ça fonctionnait.

En production, le trafic passait par un load balancer qui terminait le TLS et envoyait du HTTP clair à Apache. La logique de redirection était basée sur %{HTTPS} et supposait que si Apache voyait HTTP, le client était en HTTP. Cette hypothèse était fausse. Le client était déjà en HTTPS ; Apache ne le savait juste pas.

La règle de redirection forçait HTTPS, le load balancer envoyait HTTP à Apache, Apache forçait encore HTTPS, et le client rebondissait entre les endpoints jusqu’à ce que le navigateur abandonne. Le monitoring montrait des instances « saines » parce qu’Apache répondait rapidement — avec des redirections. De l’extérieur, le site était inutilisable.

La correction a été ennuyeuse : configurer Apache pour faire confiance à X-Forwarded-Proto (ou faire la canonicalisation exclusivement au load balancer), puis simplifier .htaccess pour ne garder que la réécriture WordPress. La redirection a été déplacée vers la couche edge où le scheme est connu. Ils ont aussi ajouté un check synthétique qui échoue en cas de redirections excessives, parce que « 200 n’est pas la même chose qu’utilisable ».

Mini-récit 2 : L’optimisation qui s’est retournée contre eux

Un ingénieur orienté performance voulait réduire la charge PHP. Il a ajouté des en-têtes de cache agressifs dans .htaccess pour tout sous /wp-content/, plus un ensemble de règles de réécriture pour servir des assets précompressés. Sur le papier : moins de requêtes, pages plus rapides, utilisateurs plus contents.

Puis sont arrivées les pannes silencieuses. Un plugin a été mis à jour et a changé des noms de fichiers tout en réutilisant des chemins. Les en-têtes de cache étaient réglés sur des valeurs immuables à longue durée. Les navigateurs gardaient du JavaScript obsolète. Certains utilisateurs ne pouvaient pas soumettre de formulaires car le code de validation côté client était ancien. Les tickets de support ont commencé : « le bouton de paiement ne marche pas ». L’incident n’était pas une panne nette ; c’était pire — une casse partielle qui échappait au monitoring.

Quand ils ont essayé de « réparer vite », ils ont purgé les caches CDN, mais les caches des navigateurs sont restés empoisonnés. Ils ont dû revenir sur les en-têtes, incrémenter les versions d’assets, et envoyer des instructions ciblées pour forcer le cache. Le vrai coût n’était pas le CPU ; c’était la confiance des utilisateurs et le temps.

Leçon apprise : le caching est un changement produit. Si vous ne contrôlez pas la version des assets de bout en bout, ne définissez pas des durées de cache héroïques à l’origine via .htaccess. Utilisez la couche de cache de la plateforme (CDN) avec des défauts sensés et une stratégie d’invalidation explicite, ou gardez des TTL courts.

Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Dans une autre société, le site WordPress était soumis à un processus de gestion des changements interne. Ce n’était pas fancy. C’était juste cohérent : chaque modification de config, y compris .htaccess, passait par un petit repo et un job de déploiement qui archivait les versions précédentes sur le serveur.

Un vendredi après-midi, un sous-traitant a appliqué un « pack de durcissement » qui incluait le blocage d’accès à certains fichiers PHP et l’ajout de règles restrictives sous /wp-admin/. Cela a immédiatement verrouillé les admins — le même jour où des mises à jour de contenu étaient planifiées. Prévisible, tout le monde a blâmé WordPress.

La personne d’astreinte n’a pas débattu. Elle a récupéré l’artifact .htaccess connu bon dans les logs de déploiement, l’a restauré, et a confirmé la récupération en quelques minutes. Pas d’archéologie. Pas de suppositions. Pas d’exploration SSH héroïque dans plusieurs instances.

Puis, calmement, ils ont construit un plan de test : vérifier la connexion admin, les appels API REST utilisés par l’éditeur, les uploads, et le cron. Les modifications du sous-traitant ont été réintroduites derrière une staging, ajustées, et déployées avec un plan de rollback. Ce n’était pas glamour. Ça a fonctionné.

Erreurs courantes : symptôme → cause racine → correction

Cette section existe parce que la plupart des cassages .htaccess se ressemblent. Si vous reconnaissez le symptôme, vous pouvez éviter beaucoup de drame.

500 Internal Server Error immédiatement après avoir édité .htaccess

  • Symptôme : Chaque requête renvoie 500 ; le log d’erreur Apache pointe vers .htaccess.
  • Cause racine : Directive invalide (typo), module non activé (souvent mod_rewrite), ou directive non autorisée dans le contexte .htaccess.
  • Correction : Vérifier le log d’erreur pour la directive exacte, activer le module (a2enmod rewrite), ou déplacer la directive dans la config du vhost si elle n’est pas permise.

403 Forbidden sur tout, y compris la page d’accueil

  • Symptôme : 403 pour / et les assets ; parfois seulement après avoir ajouté des règles « deny ».
  • Cause racine : Require all denied trop large, ancien style Deny from all mal appliqué, ou permissions de traversée de répertoire cassées.
  • Correction : Retirer/limiter les blocs deny ; vérifier les permissions d’exécution des répertoires avec namei -l ; s’assurer que le vhost a Require all granted pour le DocumentRoot.

Les permaliens jolis renvoient 404 d’Apache, mais /index.php fonctionne

  • Symptôme : /about/ échoue avec 404 côté serveur ; /index.php?p=123 fonctionne.
  • Cause racine : Réécriture non exécutée : mod_rewrite désactivé, AllowOverride n’autorisant pas la réécriture, ou mauvais RewriteBase parce que WordPress est dans un sous-répertoire.
  • Correction : Activer rewrite ; mettre AllowOverride All ou au moins AllowOverride FileInfo ; définir RewriteBase /subdir/ quand WordPress n’est pas à la racine du vhost.

Boucle de redirection infinie (le navigateur dit « trop de redirections »)

  • Symptôme : Boucle entre http/https ou entre www et non-www.
  • Cause racine : Canonicalisation configurée à plusieurs couches (plugin + .htaccess + load balancer), ou détection du scheme incorrecte derrière la terminaison TLS.
  • Correction : Choisir une couche pour canonicaliser. S’assurer que home/siteurl de WordPress correspondent. Si derrière un proxy, configurer les en-têtes forward de confiance et baser les redirections sur eux.

L’admin fonctionne, mais les uploads et images renvoient 403

  • Symptôme : Le site charge, mais la médiathèque affiche des images cassées ; l’accès direct à /wp-content/uploads/... renvoie 403.
  • Cause racine : Une règle deny destinée aux fichiers PHP ou fichiers dotfiles correspond trop largement, ou les permissions sur uploads sont incorrectes après une migration.
  • Correction : Restreindre le FilesMatch aux fichiers sensibles spécifiques ; vérifier propriété et permissions sur wp-content/uploads.

Le site est « up » mais lent après ajout de règles de sécurité/caching dans .htaccess

  • Symptôme : TTFB augmenté ; CPU en pic ; logs montrant beaucoup de réécritures internes.
  • Cause racine : Règles de réécriture provoquant des vérifications système de fichiers excessives, ou chaînes de redirection ; possible que le fichier .htaccess ait désormais des regex coûteuses évaluées à chaque requête.
  • Correction : Simplifier les règles ; déplacer la logique lourde dans la config du vhost ; préférer des blocs d’emplacement explicites au reverse proxy ; mesurer avec les logs d’accès et les temps de réponse.

Renforcer sans casser : en-têtes, contrôles d’accès et limites

Le durcissement n’est pas du copier-coller. Le durcissement, c’est du threat modeling plus des tests de compatibilité. WordPress a une UI admin, une API REST, un endpoint cron, des flux de mise à jour de plugins, des uploads, et parfois un plugin de cache qui attend certains en-têtes. Si vous le verrouillez comme un site statique, il se comportera comme une porte verrouillée : fermée.

Règles généralement sûres

  • Désactiver l’indexation des répertoires avec Options -Indexes. Cela empêche la navigation accidentelle dans des répertoires sans fichier index.
  • Bloquer l’accès aux fichiers sensibles explicites comme wp-config.php et .env. Utilisez des correspondances explicites, pas des jokers larges.
  • Ajouter des en-têtes de sécurité non invasifs (X-Content-Type-Options, Referrer-Policy). Ils sont à faible risque et à forte valeur.

Règles risquées à traiter comme des changements, pas des défauts

  • Bloquer xmlrpc.php aveuglément. Certains sites l’utilisent encore (applications mobiles, intégrations). Si vous le bloquez, vérifiez que rien n’en dépend.
  • Allowlisting IP pour /wp-admin/. Super pour des sites internes ; cauchemar de support pour des équipes distribuées et la réponse à incidents. Si vous le faites, incluez un chemin « break-glass ».
  • Blocs complexes par user-agent ou query-string. Ils tendent à bloquer des requêtes légitimes et créent des débogages « ça marche sur ma machine ».
  • En-têtes de cache trop agressifs à l’origine. Utiles quand vous contrôlez le versionnage ; dangereux quand vous ne le contrôlez pas.

Où mettre la logique : .htaccess vs vhost vs load balancer

Si vous contrôlez la config serveur, préférez la config du vhost plutôt que .htaccess. C’est plus rapide (pas de recherches filesystem à chaque requête) et plus auditable. Utilisez .htaccess comme couche de compatibilité, pas comme moteur principal de politiques.

Si vous avez un load balancer ou un CDN, les redirections canoniques (www/non-www, http/https) appartiennent généralement là. Cette couche voit le vrai scheme client et peut imposer un comportement cohérent entre origines.

Une citation (idée paraphrasée) : Les systèmes échouent de façons surprenantes ; la résilience vient du fait d’attendre la panne et de concevoir pour la récupération. — idée paraphrasée associée à la réflexion sur la fiabilité opérationnelle de John Allspaw.

Blague courte #2 : La façon la plus rapide d’apprendre les règles de réécriture est de provoquer une boucle de redirection et de regarder votre navigateur devenir un athlète cardio.

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

Checklist A : Remettre le site en service (mode incident 15 minutes)

  1. Confirmer le symptôme localement avec curl vers 127.0.0.1 pour éviter le bruit du CDN.
  2. Surveiller les logs d’erreur Apache et reproduire la requête une fois.
  3. Sauvegarder le .htaccess courant avec un horodatage.
  4. Désactiver temporairement le .htaccess en le renommant. Si le service revient, vous avez isolé la cause.
  5. Restaurer le bloc de réécriture WordPress minimal et retester la page d’accueil et un permalien.
  6. Confirmer les points d’entrée admin comme /wp-login.php et /wp-admin/.
  7. Arrêtez-vous là. N’essayez pas d’« améliorer » pendant l’incident. Stabilisez d’abord.

Checklist B : Diagnostiquer la cause racine réelle (après restauration du service)

  1. Comparer le fichier cassé avec le défaut. Identifier quel bloc a introduit le comportement.
  2. Vérifier les modules activés (rewrite, headers) et la compatibilité de la version du serveur.
  3. Revoir le comportement du proxy/LB : terminaison TLS, en-têtes forwardés, redirections canoniques.
  4. Tester en staging avec le même vhost et la même configuration d’en-têtes proxy. « Staging sans le load balancer » n’est pas du staging ; c’est de l’artisanat.
  5. Ajouter des tests synthétiques : un pour le nombre de redirections, un pour la page de connexion, un pour un permalien, un pour un asset statique.

Checklist C : Réintroduire les règles nécessaires en sécurité

  1. Ajouter une seule modification logique à la fois (un bloc de redirection, un bloc deny, un bloc d’en-têtes).
  2. Après chaque changement, lancer trois contrôles : page d’accueil, un permalien, et wp-login.
  3. Surveiller les logs pendant les tests. Apache vous dira souvent ce que le navigateur ne dira pas.
  4. Préférer la config vhost pour les règles permanentes si vous la contrôlez ; garder .htaccess minimal.
  5. Documenter pourquoi la règle existe dans des commentaires. Le futur vous oubliera, et le futur sera d’astreinte.

FAQ

1) Puis-je simplement supprimer .htaccess ?

Vous pouvez, et c’est un bon test d’isolation. Mais si vous dépendez des permaliens jolis, le supprimer dégradera généralement le routage vers des URLs « plain » ou cassera la résolution des pages. Utilisez la suppression/renommage pour confirmer la causalité, puis restaurez le minimal par défaut.

2) Pourquoi une seule ligne dans .htaccess a-t-elle fait tomber tout le site ?

Parce qu’Apache parse .htaccess pendant le traitement de la requête. Une erreur de syntaxe ou une directive inconnue peut faire qu’Apache refuse la requête (souvent avec un 500) avant que WordPress ne s’exécute. C’est un échec rapide, pas une dégradation gracieuse.

3) Je suis sur Nginx. Pourquoi mon changement .htaccess ne fait rien ?

Nginx ne lit pas .htaccess. Si vous êtes derrière Nginx (ou un hébergeur géré utilisant Nginx), les règles équivalentes doivent être implémentées dans la config Nginx, pas dans un fichier que WordPress attend qu’Apache lise.

4) Quel est le contenu par défaut le plus sûr pour .htaccess WordPress ?

Le bloc de réécriture WordPress montré ci-dessus. C’est l’ensemble minimal qui fait fonctionner les permaliens. N’ajoutez que les règles supplémentaires que vous pouvez justifier et tester.

5) Dois-je mettre des en-têtes de sécurité dans .htaccess ?

C’est acceptable si vous ne pouvez pas éditer la config vhost, mais gardez cela minimal. Certains en-têtes (en particulier Content Security Policy) peuvent casser des plugins, des éditeurs et du contenu embarqué. Commencez par des en-têtes à faible risque et étendez seulement après tests.

6) Pourquoi j’obtiens une boucle de redirection après avoir forcé HTTPS ?

Le plus souvent : TLS est terminé au niveau du load balancer, Apache voit du HTTP backend, et votre logique de redirection utilise %{HTTPS} au lieu des en-têtes forwarded de confiance. Corrigez la canonicalisation à l’edge, ou apprenez à Apache le vrai scheme.

7) Un plugin peut-il réécrire mon .htaccess automatiquement ?

Oui. Beaucoup de plugins de cache et de sécurité modifient .htaccess. C’est pratique jusqu’à ce que ça ne le soit plus. Si vous l’autorisez, traitez-le comme du code : suivez les changements, gardez des sauvegardes, et comprenez ce que le plugin écrit.

8) Et si je ne peux pas accéder à wp-admin pour « Enregistrer les permaliens » et régénérer .htaccess ?

Restaurez manuellement le bloc par défaut (comme montré), puis récupérez l’accès admin. Alternativement, utilisez WP-CLI pour ajuster les réglages des permaliens, mais la réécriture côté webserver doit encore être correcte sinon WordPress ne verra pas les routes prévues.

9) AllowOverride All est-ce une mauvaise idée ?

Pratique opérationnellement, sensible côté sécurité. Si possible, segmentez : activez seulement ce dont vous avez besoin (AllowOverride FileInfo pour la réécriture, peut-être Options si nécessaire). Moins de directives autorisées réduit la surface d’impact d’une mauvaise modification.

10) Comment éviter que cela se reproduise ?

Cessez de traiter .htaccess comme un brouillon. Mettez-le sous contrôle de version, déployez-le comme configuration, et ajoutez un mécanisme de rollback. Ajoutez aussi du monitoring synthétique qui détecte rapidement les boucles de redirection et les pics de 500.

Conclusion : étapes suivantes pour éviter une récurrence

Si votre site WordPress est tombé après une modification de .htaccess, la réparation n’est pas mystique. C’est de la discipline :

  1. Restaurez le service rapidement en sauvegardant et en revenant au bloc de réécriture WordPress par défaut.
  2. Utilisez les logs comme source de vérité. Apache vous dit quand il ne peut pas parser ou appliquer des directives.
  3. Choisissez une couche pour les redirections (edge ou origine), et cessez d’empiler la canonicalisation à trois endroits.
  4. Durcissez avec retenue. Bloquez des fichiers sensibles explicites, désactivez l’indexation, ajoutez quelques en-têtes sûrs. Gardez les gestes héroïques pour la staging.
  5. Opérationnalisez le fichier : contrôle de version, déploiement automatisé, et un artifact de rollback que vous pouvez appliquer à moitié endormi.

L’objectif n’est pas de ne jamais casser .htaccess. L’objectif est de rendre sa casse non catastrophique. Les systèmes de production récompensent la correction ennuyeuse. Ils punissent l’ingéniosité avec intérêt.

← Précédent
IPv6 dans Docker : l’activer correctement (et éviter les fuites surprises)
Suivant →
« 640 KB suffisent » : le mythe de la citation qui ne meurt pas

Laisser un commentaire