Un site WordPress parfaitement codé, avec des nonces partout, des sorties échappées et des routes REST bien protégées, reste vulnérable à certaines attaques que seul le navigateur peut bloquer. Le clickjacking, l’exécution de script injecté par une extension tierce compromise, ou le vol de cookie sur une connexion mal sécurisée ne se règlent pas uniquement dans le code PHP : ils se règlent aussi dans les en-têtes de réponse HTTP.
Ces en-têtes indiquent au navigateur comment se comporter face à la page reçue : autoriser ou non son affichage dans une iframe, n’exécuter que les scripts d’origines déclarées, forcer HTTPS pour toutes les requêtes futures. Ils forment une deuxième ligne de défense, indépendante du code de l’application, et souvent négligée sur les projets WordPress.
Content-Security-Policy : la plus puissante, la plus délicate
La Content-Security-Policy, ou CSP, définit la liste des sources autorisées à fournir des scripts, des styles, des images ou des polices pour la page. Bien configurée, elle empêche l’exécution d’un script injecté par une faille XSS, même si la faille existe dans le code. Mal configurée, elle casse silencieusement des fonctionnalités entières : un plugin de statistiques qui charge un script externe, une police Google Fonts, une iframe de vidéo intégrée peuvent tous se retrouver bloqués.
add_action( 'send_headers', function () {
header(
"Content-Security-Policy: default-src 'self'; " .
"script-src 'self' https://www.googletagmanager.com; " .
"style-src 'self' 'unsafe-inline'; " .
"img-src 'self' data: https:;"
);
} );
La meilleure méthode pour introduire une CSP consiste à démarrer en mode Content-Security-Policy-Report-Only, qui journalise les violations sans bloquer réellement le contenu, puis à ajuster la politique pendant plusieurs semaines avant de passer en mode bloquant.
HSTS : forcer HTTPS durablement
L’en-tête Strict-Transport-Security indique au navigateur de ne plus jamais tenter de charger le site en HTTP, même si un lien ou un favori pointe vers cette version, pendant une durée définie en secondes.

add_action( 'send_headers', function () {
header( 'Strict-Transport-Security: max-age=31536000; includeSubDomains' );
} );
Cet en-tête ne doit être ajouté qu’une fois HTTPS totalement fiable sur le domaine et tous ses sous-domaines concernés : une erreur de certificat après activation de HSTS rend le site inaccessible pendant toute la durée du max-age, sans possibilité de repli en HTTP.
X-Frame-Options et X-Content-Type-Options
X-Frame-Options empêche l’affichage du site dans une iframe sur un domaine tiers, ce qui referme la porte au clickjacking, une technique qui superpose des éléments invisibles pour piéger un clic. X-Content-Type-Options: nosniff empêche le navigateur de deviner le type d’un fichier au-delà de ce que déclare son en-tête Content-Type, ce qui referme certaines attaques par confusion de type MIME.
| En-tête | Rôle | Valeur type |
|---|---|---|
| X-Frame-Options | Empêche l’affichage en iframe externe | SAMEORIGIN |
| X-Content-Type-Options | Empêche le sniffing de type MIME | nosniff |
| Referrer-Policy | Limite les informations transmises au clic sur un lien | strict-origin-when-cross-origin |
Ajouter les en-têtes sans toucher au serveur
Sur un projet où l’on ne maîtrise pas la configuration Nginx ou Apache, le hook send_headers permet d’ajouter ces en-têtes directement depuis un plugin ou le fichier functions.php, ce qui rend la configuration versionnable avec le reste du code.
add_action( 'send_headers', function () {
header( 'X-Frame-Options: SAMEORIGIN' );
header( 'X-Content-Type-Options: nosniff' );
header( 'Referrer-Policy: strict-origin-when-cross-origin' );
} );
Sur un projet où l’accès au serveur est possible, ajouter ces en-têtes directement dans la configuration Nginx ou dans le fichier .htaccess reste préférable : ils s’appliquent alors même aux ressources statiques servies en dehors du cycle de chargement de WordPress.
Vérifier ce qui est réellement envoyé
Un en-tête ajouté dans le code n’est pas toujours celui qui arrive jusqu’au navigateur : un CDN, un reverse proxy ou un cache de page peuvent le réécrire ou le supprimer. Des outils comme securityheaders.com permettent de vérifier en quelques secondes les en-têtes réellement reçus par un client externe, et attribuent une note globale qui aide à prioriser les réglages manquants.
- Tester après chaque changement de configuration serveur ou de CDN
- Vérifier la page d’accueil, une page d’article et une page de panier ou de compte, car certains caches diffèrent selon le contexte
- Documenter la politique CSP choisie pour que l’équipe sache pourquoi telle source est autorisée
Sur mes projets, j’ajoute toujours ces en-têtes en mode « report only » d’abord, je laisse tourner une à deux semaines, puis je bascule en mode strict. Ça évite le coup de fil du client un vendredi soir parce qu’une iframe de paiement ne charge plus.
En résumé
Les en-têtes HTTP de sécurité ne remplacent aucune bonne pratique de code, mais ils referment des angles morts que le PHP seul ne peut pas couvrir : l’affichage en iframe, l’exécution de script sur une origine imprévue, le chargement accidentel en HTTP. Cinq en-têtes bien choisis, testés progressivement en mode non bloquant, suffisent à faire franchir un vrai palier de sécurité à un site WordPress sans toucher à une seule ligne de logique métier.