Accueil › WordPress › .htaccess
Chez web perf, l'optimisation technique d'un site WordPress repose sur des directives serveur précises. Le fichier .htaccess pilote les permaliens, la compression, le cache, les redirections et une partie de la sécurité. Bien configuré, il supprime des allers-retours inutiles entre le navigateur et le serveur. Mal écrit, il provoque une page blanche, une boucle de redirection ou une erreur 500.
Il ne fonctionne que sous Apache et les environnements compatibles comme LiteSpeed : Nginx et Caddy n'utilisent pas ce fichier.
htaccess WordPress : rôle du fichier et méthode
Le fichier applique des règles avant même le traitement du PHP : permaliens, compression, cache navigateur, restrictions d'accès, en-têtes HTTP. Il se trouve à la racine, aux côtés de wp-admin, wp-content et wp-includes, et reste masqué dans beaucoup de clients FTP. S'il manque, un simple enregistrement des permaliens le régénère.
Les règles de méthode
- Conservez une copie datée avant chaque intervention.
- Ajoutez un bloc à la fois, videz le cache navigateur (Ctrl+F5), puis testez l'accueil, des articles, les formulaires et
/wp-admin/. - N'écrivez jamais dans les blocs
# BEGIN … / # END …générés par WordPress ou par vos plugins de cache et de sécurité : ils sont réécrits automatiquement. Placez vos règles avant# BEGIN WordPress. - Entourez les directives de
<IfModule>: un module absent provoque sinon une erreur 500. - Attention au cache du
.htaccesschez certains hébergeurs : tout peut sembler fonctionner, puis le site tomber quelques minutes plus tard. - Encodage UTF-8 sans BOM, guillemets droits uniquement. Sur un site à fort trafic, validez d'abord en préproduction.
Le .htaccess est relu à chaque requête : si vous maîtrisez le vhost, placez-y ces directives.
Améliorer le chargement des pages
Redirections HTTPS et www, compatibles HSTS
La première redirection doit rester sur le même hôte (HTTP vers HTTPS), car le navigateur n'accepte l'en-tête HSTS que sur une réponse HTTPS. Le passage vers www se fait ensuite. Cela coûte un saut de plus, mais c'est indispensable au préchargement HSTS. Testez en 302 avant de passer en 301, et ne redirigez pas tous les sous-domaines vers www..
<IfModule mod_rewrite.c>
RewriteEngine On
# 1) HTTP -> HTTPS, même hôte
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP:X-Forwarded-Proto} !=https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# 2) domaine nu -> www
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [L,R=301]
</IfModule>
La condition X-Forwarded-Proto évite les boucles derrière un CDN ou un proxy.
Compression : Brotli en priorité, gzip en repli
Deflate (gzip) compresse HTML, CSS et JavaScript ; Brotli réduit davantage quand le module est disponible. Gardez toujours le repli gzip : sans lui, les clients sans Brotli reçoivent du contenu non compressé. Le vieux module mod_gzip est obsolète, et ne compressez jamais les formats déjà compressés (JPG, PNG, WebP, AVIF, WOFF2, PDF).
<IfModule mod_brotli.c>
BrotliCompressionQuality 5
AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml
</IfModule>
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml
</IfModule>
Cache navigateur : une seule stratégie
Les en-têtes d'expiration évitent au navigateur de solliciter le serveur à chaque affichage. Choisissez une stratégie : empiler mod_expires et Header set Cache-Control crée des règles contradictoires. Ne mettez jamais le HTML en cache long, sinon les mises à jour deviennent invisibles ; le cache de pages relève d'un plugin ou du CDN. WordPress ajoute ?ver= aux CSS et JS : un cache long est sûr uniquement si les fichiers sont versionnés.
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/avif "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType text/javascript "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(avif|webp|jpe?g|png|svg|woff2?)$">
Header append Cache-Control "public, immutable"
</FilesMatch>
</IfModule>
Pensez à déclarer les types MIME modernes (AVIF, WebP, WOFF2) et supprimez les vestiges image/jpg, text/x-javascript et Flash. Ne supprimez pas les ETag : ce sont des validateurs qui permettent les réponses 304, pas une « date de péremption ».
Le .htaccess ne corrige ni une image démesurée ni une base de données encombrée. Il s'inscrit dans une méthode globale : hébergement robuste, HTTP/2 ou HTTP/3, PHP-FPM et OPcache, cache de pages, nettoyage des extensions et allègement du balisage.
htaccess WordPress : renforcer la sécurité sans bloquer le site
Fichiers sensibles et exécution de PHP
Configuration, journaux, sauvegardes SQL et fichiers cachés ne doivent jamais être accessibles. Interdisez aussi l'exécution de PHP dans wp-content/uploads : c'est le vecteur le plus courant de webshells. Retirez xmlrpc.php de la liste si vous utilisez Jetpack ou l'application mobile WordPress.
Options -Indexes
<FilesMatch "^(wp-config(-sample)?\.php|readme\.html|license\.txt|xmlrpc\.php)$">
Require all denied
</FilesMatch>
<FilesMatch "^\.">
Require all denied
</FilesMatch>
<FilesMatch "\.(log|sql|bak|old|orig|swp|env)$">
Require all denied
</FilesMatch>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^wp-content/uploads/.*\.(php|phtml|phar)$ - [F,NC,L]
RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)$ [NC]
RewriteRule ^ - [F]
</IfModule>
Si Options provoque une erreur 500, l'hébergeur n'autorise pas AllowOverride Options. Bloquer l'énumération ?author=N est possible, mais une règle non ancrée bloque aussi le filtre par auteur de l'administration : ancrez le paramètre et excluez /wp-admin/.
En-têtes HTTP de sécurité
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" env=HTTPS
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Header always set X-XSS-Protection "0"
Header always set Cross-Origin-Opener-Policy "same-origin-allow-popups"
Header always unset X-Powered-By
</IfModule>
- HSTS : déployez progressivement (300 s, puis 1 jour, puis 1 an). Le minimum attendu par les audits est de 6 mois (15768000). Le
preloadexigeincludeSubDomains, unmax-aged'au moins 1 an et une soumission sur hstspreload.org. Il est quasi irréversible : n'y recourez que si tous vos sous-domaines sont en HTTPS.Header alwaysgarantit que le 301 verswwwporte aussi HSTS. - X-Frame-Options :
SAMEORIGINplutôt queDENY, qui casse le Personnaliseur. - X-XSS-Protection : l'auditeur XSS est supprimé des navigateurs modernes et jugé dangereux. La seule valeur sûre est
0. - COOP :
same-origin-allow-popupspréserve les popups de paiement et de connexion sociale. CORPsame-sitebloque le chargement de vos médias par vos autres domaines. COEPrequire-corpcasse YouTube ou Google Fonts et n'est utile que pour l'isolation cross-origin : inutile sur un WordPress classique.
Content-Security-Policy : deux temps
Un script-src 'self' seul casse WordPress : le cœur, les thèmes et les plugins produisent des scripts, des styles et des attributs style="" inline, et l'administration exige de l'inline, parfois de l'eval. Appliquez donc une politique minimale, et observez une politique stricte en Report-Only avant tout durcissement.
Header always set Content-Security-Policy "frame-ancestors 'self'; object-src 'none'; base-uri 'self'; upgrade-insecure-requests"
Header always set Content-Security-Policy-Report-Only "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'"
La politique stricte inventorie ce qui bloquerait (console F12 ou report-uri). Excluez /wp-admin/, wp-login.php et l'API REST. La passer en mode bloquant suppose des nonces ou des hashes générés en PHP, incompatibles avec un cache de pages complet. Les audits signaleront unsafe-inline tant que ce chantier n'est pas mené : c'est un compromis assumé.
Au-delà du .htaccess
- SRI : l'attribut
integrityse pose dans le HTML, pas dans le.htaccess. Le plus robuste est d'héberger bibliothèques et polices en local ; SRI est impossible pour les scripts dont le contenu change (Google Tag Manager, Analytics). - WordPress :
DISALLOW_FILE_EDIT, permissions 755/644 (640 pourwp-config.php), mises à jour, 2FA des administrateurs. - DNS et e-mail : DNSSEC, enregistrements CAA, SPF, DKIM, DMARC (progression
p=none,quarantine,reject), MTA-STS et TLS-RPT.
Gérer les redirections 301 et les erreurs
Une redirection 301 transfère l'autorité d'une ancienne URL vers la nouvelle après un changement de permalien.
Redirect 301 /ancienne-page/ https://exemple.test/nouvelle-page/
L'ordre des directives compte : une règle générique placée trop haut intercepte des requêtes destinées à une règle plus précise. Évitez les chaînes de redirections, qui ralentissent la navigation et gaspillent le budget d'exploration des robots. Dressez un inventaire des URL stratégiques et renvoyez une page introuvable vers un contenu proche, non vers l'accueil. Une page 404 sur mesure aide l'internaute sans remplacer une redirection technique.
En cas d'erreur 500, restaurez la sauvegarde, puis inspectez le journal d'erreurs, les guillemets, les balises et les modules réellement activés. Tenez un journal des révisions (heure, instruction ajoutée) pour isoler la cause.
Les erreurs classiques d'un .htaccess copié-collé
| Erreur | Conséquence | Correction |
|---|---|---|
| Guillemet manquant dans une CSP | Erreur 500 | Relire chaque directive, tester bloc par bloc |
Header set X-Content-Type-Options: nosniff (deux-points) | En-tête invalide | Header always set X-Content-Type-Options "nosniff" |
Deux CSP ou DENY et SAMEORIGIN mélangés | Comportement contradictoire, admin cassée | Une seule politique, SAMEORIGIN |
RewriteEngine On oublié | Règles ignorées sans message | Toujours l'écrire avant les règles |
Règles après # BEGIN WordPress | Jamais évaluées pour les pages WordPress | Les placer avant |
Header hors IfModule | Erreur 500 si le module manque | Entourer de <IfModule mod_headers.c> |
| Cache long sur le HTML | Mises à jour invisibles | Cache long réservé aux fichiers statiques |
Tester après chaque déploiement
curl -sI -H "Accept-Encoding: br" https://www.example.com/puis avecgzip: vérifiezContent-EncodingetCache-Control.curl -sIL http://example.com/: le premier saut doit êtrehttps://example.com/, avec HSTS sur les réponses HTTPS.- securityheaders.com, Mozilla Observatory, hstspreload.org, SSL Labs.
- Lighthouse et PageSpeed Insights sur plusieurs terminaux ; contrôlez l'accueil, les formulaires et
/wp-admin/.
FAQ
Où se trouve le fichier .htaccess de WordPress ?
À la racine du site, à côté de wp-admin, wp-content et wp-includes. Il est masqué (nom commençant par un point) : activez l'affichage des fichiers cachés dans votre client FTP. S'il manque, enregistrer les permaliens dans l'administration le régénère.
Comment corriger une erreur 500 après une modification du .htaccess ?
Restaurez la sauvegarde ou retirez le dernier bloc ajouté, puis consultez le journal d'erreurs Apache. Vérifiez les guillemets droits, les balises fermées, l'encodage UTF-8 sans BOM et la présence des modules utilisés (entourez les directives de IfModule).
Le préchargement HSTS (preload) est-il réversible ?
Difficilement : le retrait des listes des navigateurs prend plusieurs mois. Ne l'activez que si le domaine et tous ses sous-domaines, présents et futurs, sont servis en HTTPS valide.