Les en-têtes de sécurité HTTP sont quelques lignes envoyées avec chaque page. Ils ne réparent pas une extension vulnérable, mais ils demandent au navigateur de vos visiteurs de refuser certains comportements dangereux : chargement en HTTP, affichage dans un cadre piégé, exécution d’un script venu d’ailleurs. Cet article explique ce que fait chaque en-tête, ce que WordPress envoie déjà, les valeurs de départ raisonnables et la méthode pour les tester sans casser l’administration.
Définition express : En-têtes de sécurité HTTP. Solution prête à l’emploi : En-têtes de sécurité WordPress avec WP Masquerade.
Ce que ces en-têtes protègent, et ce qu’ils ne protègent pas
Un en-tête de sécurité est une consigne : le serveur l’envoie, le navigateur l’applique. La protection porte donc sur le visiteur et sur la façon dont vos pages sont affichées. Un robot qui attaque directement wp-login.php ou une extension ignore totalement ces consignes.
- Ils limitent l’impact d’une faille XSS, d’un détournement de clic (clickjacking) ou d’une interception sur un Wi-Fi public.
- Ils ne bloquent pas une injection SQL, un téléversement de fichier malveillant ou une élévation de privilèges dans une extension : seule la mise à jour corrige ces failles.
- Ils s’ajoutent aux autres couches (mises à jour, double authentification, sauvegardes), sans les remplacer.

Ce que WordPress envoie déjà
WordPress pose quelques en-têtes lui-même, mais seulement sur certaines réponses. Les connaître évite d’en ajouter en double, avec des valeurs contradictoires.
| Réponse | En-tête envoyé par WordPress | Depuis |
|---|---|---|
Administration (admin_init) et page de connexion (login_init) | X-Frame-Options: SAMEORIGIN | WordPress 3.1.3 |
| Administration et page de connexion | Content-Security-Policy: frame-ancestors 'self'; | WordPress 6.9 |
| Administration et page de connexion | Referrer-Policy: strict-origin-when-cross-origin | WordPress 4.9 (valeur actuelle depuis 4.9.5) |
API REST et admin-ajax.php | X-Content-Type-Options: nosniff | Présent dans le code actuel |
| Pages publiques du site | Aucun de ces en-têtes par défaut |
Autrement dit, ce sont vos pages publiques qui restent nues. C’est là qu’il faut agir.
Les six en-têtes à connaître, avec des valeurs de départ
| En-tête | Rôle | Valeur de départ | Risque de casse |
|---|---|---|---|
Strict-Transport-Security | Impose HTTPS pour les visites suivantes | max-age=31536000 après une période d’essai courte | Faible si tout le site est en HTTPS, fort si un sous-domaine ne l’est pas |
X-Content-Type-Options | Interdit au navigateur de deviner le type d’un fichier | nosniff | Très faible |
X-Frame-Options | Interdit l’affichage dans un cadre d’un autre site | SAMEORIGIN | Faible ; DENY casserait l’aperçu de l’outil de personnalisation |
Referrer-Policy | Limite l’URL transmise aux sites liés | strict-origin-when-cross-origin | Très faible |
Permissions-Policy | Coupe les API sensibles du navigateur | camera=(), microphone=(), geolocation=() | Moyen si une extension utilise la géolocalisation ou la caméra |
Content-Security-Policy | Liste les sources autorisées (scripts, styles, cadres…) | D’abord en mode rapport seul | Élevé : à construire site par site |
HSTS : puissant, mais difficile à annuler
Strict-Transport-Security indique au navigateur de n’utiliser que HTTPS pendant max-age secondes. Le navigateur ignore cet en-tête s’il arrive en HTTP, et la toute première visite reste exposée tant qu’il ne l’a jamais reçu. Pour désactiver HSTS, il faut envoyer max-age=0 en HTTPS : impossible si votre certificat a expiré entre-temps.

La liste de préchargement hstspreload.org supprime ce point faible, avec des conditions strictes : certificat valide, redirection de HTTP vers HTTPS sur le même hôte, tous les sous-domaines en HTTPS, et un en-tête avec max-age d’au moins 31536000 secondes (un an), includeSubDomains et preload. Le site prévient que l’inscription ne s’annule pas facilement et que le retrait met des mois à atteindre les utilisateurs. Ne demandez le préchargement que si tous vos sous-domaines (messagerie web, préproduction, ancien extranet) sont en HTTPS pour longtemps.
X-Frame-Options et frame-ancestors
Les deux empêchent qu’un autre site affiche le vôtre dans un cadre invisible pour vous faire cliquer à votre insu. OWASP recommande la directive CSP frame-ancestors, qui remplace X-Frame-Options dans les navigateurs qui la prennent en charge. Envoyer les deux avec SAMEORIGIN et 'self' reproduit ce que WordPress fait déjà pour l’administration depuis la version 6.9.
Content-Security-Policy : commencer en mode rapport
La CSP est l’en-tête le plus efficace contre les scripts injectés, et le plus délicat sur WordPress : le cœur, les thèmes et beaucoup d’extensions écrivent des scripts en ligne. Une politique stricte copiée d’un tutoriel bloque souvent l’éditeur, les formulaires ou les statistiques. La bonne méthode :
- Envoyer
Content-Security-Policy-Report-Only: le navigateur signale les violations sans rien bloquer. - Ouvrir la console des outils de développement sur l’accueil, un article, le panier, l’éditeur, et noter chaque source bloquée.
- Pour collecter les rapports à distance, ajouter
report-toet l’en-têteReporting-Endpoints: sans destination, aucun rapport n’est envoyé. - Passer à
Content-Security-Policyseulement quand la console reste propre sur tous les parcours importants.
Depuis WordPress 5.7, les fonctions wp_get_script_tag(), wp_get_inline_script_tag() et le filtre wp_script_attributes permettent d’ajouter un attribut nonce aux scripts chargés proprement. Les scripts écrits directement dans le HTML par un thème ou une extension n’en profitent pas : c’est pourquoi beaucoup de politiques WordPress conservent 'unsafe-inline' au début, ce qui affaiblit la protection contre les XSS.
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'
Ce point de départ est volontairement permissif : il sert à mesurer, pas à protéger. Resserrez-le ensuite source par source (domaine des polices, outil de statistiques, passerelle de paiement).
Les en-têtes à ne plus ajouter
Beaucoup de guides anciens et d’extensions proposent encore des en-têtes abandonnés. Les ajouter ne protège plus rien, et l’un d’eux peut même nuire.
| En-tête | Statut actuel | Que faire |
|---|---|---|
X-XSS-Protection | Déprécié et non standard (MDN). Peut créer des failles XSS sur des sites sains. | Ne pas l’envoyer, ou l’envoyer à 0. Utiliser une CSP. |
Expect-CT | Déconseillé par OWASP, qui recommande de le retirer. | Supprimer. |
Feature-Policy | Remplacé par Permissions-Policy, à la syntaxe différente. | Convertir, par exemple camera=(). |
X-Powered-By et Server détaillé | Pas des en-têtes de sécurité : ils annoncent la version de PHP ou du serveur. | Retirer (expose_php = Off dans PHP) ou neutraliser. |
Où les configurer : serveur, PHP ou extension
Le choix dépend de votre hébergement. La règle générale : plus la consigne est posée tôt, plus elle couvre de réponses.
- Au niveau du serveur (
.htaccessApache ou LiteSpeed, configuration Nginx) : couvre aussi les fichiers statiques, les erreurs et les pages servies par un cache de pages sans passer par PHP. - En PHP (action
send_headers) : pratique sur un hébergement sans accès à la configuration, mais absent des réponses qui ne chargent pas WordPress, comme une page servie directement par un cache. - Par une extension : même limite que PHP, avec une interface. Vérifiez qu’elle n’ajoute pas les en-têtes dépréciés vus plus haut.
Apache ou LiteSpeed (.htaccess)
Avec mod_headers, la condition always ajoute l’en-tête aussi sur les réponses d’erreur et les redirections, ce qui n’est pas le cas de la condition par défaut.
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000"
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=()"
</IfModule>
Nginx
Sans le paramètre always (disponible depuis Nginx 1.7.5), add_header ne s’applique qu’aux codes 200, 201, 204, 206, 301, 302, 303, 304, 307 et 308. Autre piège : un bloc location qui définit son propre add_header n’hérite plus des en-têtes du niveau server. Nginx 1.29.3 a ajouté add_header_inherit merge; pour changer ce comportement.
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
En PHP, dans une extension maison
add_action( 'send_headers', function () {
header( 'X-Content-Type-Options: nosniff' );
header( 'Referrer-Policy: strict-origin-when-cross-origin' );
header( 'Permissions-Policy: camera=(), microphone=(), geolocation=()' );
} );
N’ajoutez pas HSTS en PHP si votre hébergeur ou votre CDN le pose déjà : deux valeurs différentes compliquent le diagnostic.
Tester sans casser le site
- En ligne de commande :
curl -I https://votre-site.fr/affiche les en-têtes réellement reçus. Testez aussi une page 404, une image dewp-content/uploadsetwp-login.php. - Dans le navigateur : onglet Réseau des outils de développement, puis la réponse du document principal.
- Avec le HTTP Observatory de Mozilla, intégré à MDN depuis juillet 2024, qui note la configuration et explique chaque point.
- Parcourez ensuite les fonctions sensibles : éditeur de blocs, outil de personnalisation, formulaires, paiement, intégrations vidéo.
- Videz tous les caches (extension, hébergeur, CDN) avant chaque test : une page en cache peut conserver les anciens en-têtes.
Avec WP Masquerade
L’extension gratuite ajoute déjà X-Content-Type-Options: nosniff dès le niveau 1 du module Durcissement. WP Masquerade Pro gère les en-têtes de sécurité depuis l’administration (HSTS, X-Frame-Options, Referrer-Policy, CSP…), sans modifier les fichiers du serveur. Comme pour toute solution en PHP, une page servie par un cache en amont peut ne pas porter ces en-têtes : vérifiez le résultat avec curl et, si besoin, consultez la page compatibilité hébergeurs et caches. Le comparatif détaille la frontière entre les versions gratuite et Pro.
Une plateforme de consentement aux cookies hébergée charge son script depuis son propre domaine, qu’il faut alors autoriser dans la CSP ; notre comparatif des plugins cookies RGPD indique quelles solutions restent sur votre serveur.
À retenir
- Les en-têtes de sécurité protègent vos visiteurs, pas votre code : ils complètent les mises à jour sans les remplacer.
- WordPress protège déjà l’administration et la connexion contre l’affichage en cadre ; vos pages publiques n’ont rien par défaut.
- Déployez d’abord nosniff, SAMEORIGIN, Referrer-Policy et Permissions-Policy, qui cassent rarement quelque chose.
- Montez HSTS par paliers et ne demandez le préchargement que si tous les sous-domaines sont en HTTPS.
- Construisez la CSP en mode rapport seul, page par page, avant de l’appliquer.
- N’envoyez plus X-XSS-Protection, Expect-CT ni Feature-Policy.
Sources
- MDN : Strict-Transport-Security (nouvel onglet)
- HSTS Preload List Submission (conditions d’inscription) (nouvel onglet)
- MDN : X-XSS-Protection (nouvel onglet)
- OWASP HTTP Headers Cheat Sheet (nouvel onglet)
Autres sources consultées : documentation officielle.


