Lundi 9h. Un client m’envoie une capture de sa Search Console : des centaines de requêtes en indonésien pointent vers des pages qui n’existent pas dans son back-office. Son WordPress est devenu une plateforme de casino en ligne. Il avait pourtant suivi les 15 points de la checklist qu’on trouve sur tous les blogs. Mot de passe en Bcrypt ? Fait. Préfixe de table changé ? Fait. Plugin de sécurité installé ? Fait aussi. Ce qui manquait, c’est ce qu’aucune checklist n’écrit : surveiller les modifications de fichiers en continu, lire les logs, et comprendre pourquoi Googlebot voit des pages qu’on n’a jamais créées.
La sécurité de WordPress, je l’aborde en dev qui a passé des nuits à nettoyer des sites, pas en consultant qui vend des audits. J’ai vu un site perdre 40 % de son trafic organique en quelques jours à cause d’un fichier xmlrpc.php laissé ouvert et exploité par un botnet. Et j’ai vu l’effet inverse : un site qui remonte dans les SERP une fois les URLs parasites nettoyées et le fichier .htaccess repris en main. Voici les 15 points que tout le monde connaît, ceux qu’on peut jeter, et ce qu’il faut mettre en place pour que ton WordPress ne devienne pas la prochaine Search Console qui nous arrive en panique un matin.
Les 15 points, version courte
Mises à jour automatiques des extensions, mot de passe administrateur solide, préfixe de base de données personnalisé, désactivation de l’éditeur de thème, suppression du plugin Hello Dolly, restriction des permissions de fichiers (755 pour les dossiers, 644 pour les fichiers), clés de salage uniques dans wp-config.php, désactivation du XML-RPC si inutile, blocage de l’indexation des répertoires, protection de wp-admin par IP ou authentification HTTP supplémentaire, limitation des tentatives de connexion, cache des pages sensibles, surveillance des modifications des fichiers core, déconnexion automatique des sessions inactives, sauvegardes chiffrées quotidiennes.
Appliqués mécaniquement, ces 15 points donnent un sentiment de sécurité. Et c’est précisément ce qui les rend dangereux.
Mettre à jour WordPress, ce n’est pas appuyer sur un bouton
Quand un client me dit « mon WordPress est à jour », je vérifie systématiquement trois choses. D’abord, le script wp-cron.php qui déclenche les mises à jour automatiques. Si le cron système n’est pas configuré, les mises à jour dépendent d’une visite du site. Ce petit détail crée une fenêtre de vulnérabilité de plusieurs heures. Ensuite, je regarde si les plugins orphelins ou premium sans licence sont eux aussi à jour. Un slider acheté il y a cinq ans sur ThemeForest et jamais mis à jour, c’est une porte ouverte. Enfin, j’ouvre le log error_log du serveur. Une mise à jour qui casse une dépendance laisse souvent une trace avant que le site ne tombe.
Le vrai boulot, c’est un staging, un test de non-régression visuel et une validation des appels API backend. Pas un clic sur « Mettre à jour maintenant ». Tu veux automatiser ? Branche un workflow GitHub Actions ou un script wp-cli qui crée un snapshot du site, applique les mises à jour, exécute une série de tests, et te notifie si la page d’accueil renvoie autre chose qu’un 200.
⚠️ Attention : une mise à jour automatique des traductions peut modifier des fichiers PHP chargés en runtime. Si tu as un plugin qui hook sur
initet dépend d’une chaîne de traduction pour une logique métier, tu peux provoquer une erreur fatale. Je l’ai vu sur une extension d’e-commerce qui utilisait les traductions pour générer des routes.
Protéger wp-admin sans pénaliser les contributeurs
On te dira de whitelister ton IP dans .htaccess pour wp-admin. Ça marche si tu es seul sur une IP fixe. Pour un site avec une équipe qui voyage, des redacteurs freelance ou des connexions mobiles, cette mesure bloque plus de collaborateurs légitimes que de bots. J’ai vu un site de média perdre une soirée entière de direct parce que l’IP du journaliste en 4G n’était pas dans la liste.
Je préfère une authentification à deux facteurs (2FA) obligatoire pour tout compte avec un rôle supérieur à éditeur, combinée à une limitation de tentatives par IP sur xmlrpc.php et wp-login.php. Pas d’IP whitelist, mais un blocage temporaire au-delà de cinq échecs, tracé dans les logs. Le confort de l’équipe compte : une sécurité qui freine les flux de publication, c’est une sécurité que quelqu’un finira par contourner en notant le mot de passe dans un fichier texte.
Pendant qu’on y est, renommer la page de login via un plugin est un cache-misère. La plupart des bots scannent les formulaires HTML, pas l’URL. Si le champ input a le même name, ça ne ralentit rien. L’effort est ailleurs : durcir la politique de mot de passe (longueur minimale, expiration tous les 90 jours pour les admins) et surtout, retirer les comptes inactifs.
Ce que Googlebot voit quand le site est infecté
Une compromission ne se limite pas à des pages parasites. Dans les cas que j’ai eus sous les yeux, l’infection injectait du contenu conditionnel : un visiteur normal voyait le site légitime, Googlebot voyait des pages de spam avec des redirections 302. C’est une technique dite de cloaking, qui exploite la différence d’user-agent. Résultat : la Search Console affiche des URLs indexées qui ne correspondent à rien dans l’admin, et le site reçoit une action manuelle « Contenu piraté ».
Pour Google, un site compromis perd la confiance, et la confiance, c’est le socle des signaux de classement. Même après nettoyage, le temps de réindexation dépend du crawl budget et de la vitesse à laquelle les nouvelles pages sont soumises. Un site lent à cause d’un plugin de sécurité mal configuré aggrave la situation : il ralentit le passage de Googlebot et prolonge la période de déclassement.
La parade : un script de surveillance qui compare quotidiennement l’empreinte SHA1 des fichiers core WordPress avec la référence officielle, et te notifie si un fichier index.php modifié apparaît dans un dossier uploads. En parallèle, un log des requêtes HTTP sortantes depuis le serveur : une infection qui envoie des données vers une IP externe se trahit toujours par une connexion inconnue.
Les plugins de sécurité alourdissent ton TTFB plus qu’ils ne protègent
C’est le paradoxe que je constate à chaque audit de performance. Les plugins de sécurité « tout-en-un » ajoutent des couches de hooks, de vérifications et parfois un WAF embarqué qui lit chaque requête entrante. Sur un site à faible trafic, le Time To First Byte passe de 200 ms à 800 ms. C’est un impact direct sur le Core Web Vitals, et donc sur ton positionnement.
Ma règle : un seul plugin de sécurité, minimal, qui fait rate limiting, renforce les en-têtes HTTP (X-Frame-Options, Content-Security-Policy, X-Content-Type-Options) et surveille les modifications de fichiers. Le pare-feu, je le place au niveau de l’hébergeur ou du reverse proxy (Cloudflare, Sucuri). Déporter la logique de blocage avant que la requête touche le serveur PHP économise des ressources et améliore le TTFB. Sur une migration récente, ce changement a fait gagner 300 ms de TTFB et nous a permis de repasser sous la barre des 2,5 secondes de LCP.
📌 À retenir : un WAF côté serveur qui parse chaque requête PHP coûte du CPU. Si ton hébergement le permet, utilise un WAF périphérique. Tu gagnes en performance et en sécurité.
Quand on parle de code, il y a des outils qui changent la donne. Pendant qu’on traquait une faille dans un thème maison, on a utilisé un outil d’analyse de code assisté par IA pour identifier des endpoints REST non protégés. C’est le genre de revue qu’on faisait avant avec grep, et que des environnements comme Claude Code ou Cursor IDE permettent de faire en parallèle sur des dossiers entiers.
Comment un thème React avec Zustand a exposé la boutique
L’autre jour, un e-commerce m’a demandé un audit après avoir migré son front vers un thème WordPress headless. Les développeurs avaient codé une SPA React qui communiquait avec l’API REST de WordPress. Pour gérer le panier, ils utilisaient Zustand et exposaient un endpoint custom pour synchroniser le state client avec le backend. Problème : l’endpoint n’était pas protégé par un nonce, et un attaquant pouvait manipuler les données de session.
C’est typique des architectures modernes où on ajoute une couche JavaScript sans reproduire les mécanismes de sécurité natifs de WordPress. Le state management côté client, c’est formidable ; mais chaque endpoint REST doit être vérifié, chaque action doit porter un nonce. Sinon, tu offres une surface d’attaque invisible aux scanners automatiques qui cherchent uniquement des wp-admin mal protégés. Le fichier functions.php du thème définissait un register_rest_route avec un callback non authentifié ; un diff rapide avec le fichier d’origine nous a permis de corriger en moins d’une heure.
Cette faille n’apparaît dans aucune checklist de 15 points. Pour la détecter, il faut lire le code du thème, comprendre l’architecture JavaScript et tester les endpoints. C’est pour ça que je dis que la sécurité WordPress, quand on utilise un front découplé, c’est 30 % de configuration et 70 % de code review.
Automatiser sans paralyser le site : la surveillance logicielle
J’en reviens à la surveillance. Sur un WordPress, le vrai indicateur d’alerte, c’est le delta de fichiers entre deux jours. Un fichier PHP qui apparaît dans /wp-content/uploads/ ou un .htaccess qui grossit de 3 ko d’un coup, c’est une infection. J’utilise un script diff exécuté par cron et envoyé sur un canal Slack dédié. Pas de plugin supplémentaire, pas de charge serveur pendant les visites.
Pour éviter de plomber le TTFB, le scan ne doit jamais s’exécuter sur une requête HTTP entrante. Découplé, en tâche de fond, avec un fichier de verrou pour éviter les chevauchements. Cette approche a l’avantage d’être pauvre en ressources et de produire une trace exploitable en cas d’incident.
La sauvegarde automatique chiffrée, c’est le dernier rempart. Elle doit être stockée hors du serveur principal, idéalement sur un bucket objet avec immutabilité activée. Un site infecté ne doit jamais pouvoir chiffrer ou supprimer ses propres sauvegardes. Si ton plugin de backup écrit dans le même espace disque, tu joues à la roulette russe.
Questions fréquentes
Faut-il utiliser un pare-feu applicatif (WAF) en plus des mesures internes ?
Oui, mais déporté. Un WAF côté hébergement ralentit PHP ; un WAF périphérique (Cloudflare, Sucuri) bloque les tentatives avant qu’elles n’atteignent le serveur. L’idéal : un WAF en périphérie pour les règles génériques, et des règles granulaires au niveau de l’application uniquement sur les endpoints sensibles comme xmlrpc.php ou ?rest_route=.
Comment réagir en cas de piratage avéré sur un site WordPress ?
Mets le site en mode maintenance avec un statut HTTP 503, sauvegarde l’état compromis pour analyse, vérifie les logs d’accès et les comptes administrateurs, restaure une sauvegarde antérieure à la compromission, change tous les mots de passe (base de données, SFTP, WordPress) et soumets une demande de réexamen dans Search Console après avoir confirmé le nettoyage.
Les hébergeurs WordPress spécialisés gèrent-ils la sécurité des fichiers ?
Ils gèrent surtout l’isolation des comptes, les mises à jour automatiques du core et le WAF serveur. La sécurité des fichiers du thème, des extensions et les failles logicielles restent à ta charge. Aucun hébergement ne peut empêcher un plugin abandonné de servir de porte d’entrée.