Quand un site WordPress est compromis, on a souvent tendance à courir dans tous les sens: changer des mots de passe, supprimer quelques fichiers, installer une “clean version”. Ce réflexe peut suffire dans les cas simples, mais il laisse un risque plus discret: le code latent. Celui qui n’explose pas tout de suite, mais qui prépare une réinfection, un backdoor, un redirection, ou une dégradation des performances que les visiteurs ressentent avant que les logs ne vous donnent des preuves.
Mon approche a été affinée après plusieurs nettoyages menés à partir de signaux différents. Parfois tout commence par des erreurs 500, parfois par des accès anormaux, parfois par une hausse de chargements lents. Et très souvent, l’important n’est pas seulement de “supprimer ce qui est visible”. L’important est de vérifier ce qui reste en mémoire du serveur, dans la base de données, dans les permissions, et parfois dans la configuration du cache, du CDN, ou même du thème.
Dans ce guide, je vais dérouler une méthode pragmatique pour nettoyer un site WordPress infecté, tout en vérifiant les performances et en cherchant l’absence de code latent.
Les premiers signaux, et pourquoi ils ne suffisent pas
Un site “infecté” est un terme large. Une infection peut signifier un ajout de code dans les fichiers PHP, une modification de .htaccess ou d’un index, un injecteur dans les thèmes ou les plugins, une altération de la base de données via des options ou des formulaires, ou encore une compromission du compte administrateur. Elle peut aussi être plus subtile: du code qui ne se déclenche que sur certains navigateurs, certains pays, ou à une heure précise.
Il y a un piège fréquent: se focaliser sur ce qui “se voit” côté front. Par exemple, une page qui redirige ou qui affiche un script supplémentaire. Une fois la redirection corrigée, le danger peut rester dans un plugin abandonné, un fichier rarement utilisé, ou une entrée d’option dans la base.
Je me souviens d’un cas où les redirections avaient disparu en supprimant un fichier suspect. Les performances, elles, continuaient de se dégrader par à-coups. Les logs indiquaient des requêtes régulières sur un chemin qui ne correspondait à rien dans l’application. Le code était encore présent, mais dormant, déclenché par une condition sur un paramètre. Tant qu’on n’a pas vérifié les performances et la présence d’accès anormaux, on passe à côté.
Le message clé: les signaux d’urgence sont utiles, mais ils ne remplacent pas une vérification structurée.
Mettre le site à l’abri avant de “nettoyer”
La phase de nettoyage est plus sûre si vous isolez le problème. Sinon, vous risquez de nettoyer, puis d’être réinfecté pendant que vous travaillez. Selon votre hébergement et votre capacité à agir vite, il y a plusieurs stratégies.
- Mettre le site en mode maintenance le temps de traiter les fichiers et la base. Couper l’accès public si possible, via un blocage au niveau du serveur (par exemple en protégeant temporairement le dossier racine avec une règle stricte) ou en limitant l’accès au site. Travailler sur une copie: si vous avez la possibilité de cloner le volume (ou au minimum de récupérer une archive de la base et des fichiers), vous gagnez en sérénité.
Un point important: gardez des traces. Avant toute suppression, conservez ce que vous supprimez ou au minimum les diffs entre votre état initial et l’état final. En cas de doute sur un fichier, vous pouvez revenir en arrière sans retenter une course contre la montre.
Je privilégie aussi un inventaire rapide: lister les fichiers récemment modifiés, les plugins activés, et les utilisateurs avec rôle administrateur. Dans un incident réel, cette liste devient un guide, pas une formalité.
Comprendre où le malware se cache le plus souvent
Dans WordPress, la surface d’attaque ne se limite pas à un seul endroit. Les patterns les plus courants se répartissent généralement ainsi:
- Fichiers PHP modifiés dans le thème, le thème enfant, ou les plugins. Scripts ajoutés dans wp-content/uploads (par exemple des fichiers “image” en réalité exécutables si les mauvaises configurations existent). Changements dans wp-config.php, parfois via insertion de code conditionnel. Altération de la base de données: options, post meta, utilisateurs, cron, ou contenu de pages. Modifications de règles serveur: .htaccess, configuration Nginx, ou configuration PHP-FPM. Accès aux identifiants: un compte administrateur compromis peut réinstaller le code dès que vous corrigez.
L’objectif n’est pas seulement de retrouver le code. L’objectif est d’éliminer les mécanismes qui permettent le retour. C’est pour cela que vérifier les performances après nettoyage a une valeur de contrôle. Si un injecteur reste actif, il peut ralentir le site même sans redirection visible.
Diagnostiquer sans casser: les contrôles qui rapportent vite
Avant de supprimer quoi que ce soit, prenez le temps de diagnostiquer. Sinon, vous risquez de supprimer “le mauvais” fichier, ou de corriger un symptôme sans traiter la cause.
Je commence par trois axes: intégrité des fichiers, état des plugins et thèmes, et comportement du serveur.
Sur l’intégrité des fichiers, l’idée est simple: comparer votre installation WordPress et vos dépendances avec une version de référence saine. WordPress fournit des checksums pour le cœur, mais pas forcément pour chaque plugin ou thème. Vous pouvez néanmoins comparer les fichiers du cœur à une archive officielle, et pour les plugins et thèmes, reconstruire un état propre en réinstallant proprement depuis le dépôt d’origine si vous pouvez le faire sans perdre de modifications légitimes.
Ensuite, l’état plugins et thèmes: regardez les plugins “actifs”, puis ceux qui ont été ajoutés récemment. Les infections profitent souvent d’un plugin abandonné, ou d’un plugin installé puis jamais mis à jour. Si vous avez un plugin premium, vous ne pourrez pas toujours récupérer une version saine identique. Dans ce cas, comparez les fichiers à la version que vous avez téléchargée depuis votre compte, ou remplacez par une installation propre.
Enfin, comportement serveur: si vous avez accès aux logs (Apache ou Nginx, accès PHP, erreurs), cherchez des motifs répétitifs, des pics sur un chemin exotique, des requêtes avec des patterns très longs, ou des erreurs spécifiques liées à du code PHP. Le malware aime déclencher des appels à des fichiers qui ne devraient pas être accessibles.
Une mini-checklist pour cadrer l’intervention
- Passez le site en maintenance ou isolez l’accès public Faites une sauvegarde complète fichiers + base Notez les plugins et thèmes actifs, et les derniers changements connus Gardez les logs d’accès et d’erreurs sur la période suspecte Préparez un plan de réinstallation propre du cœur WordPress si nécessaire
Cette liste sert à ne pas perdre du temps en plein incident.
Nettoyer les fichiers: méthode, prudence, et cas limite
Une fois l’environnement sécurisé, vous pouvez nettoyer. Dans la pratique, je fais rarement “un nettoyage à la main” au premier passage sur un site complexe. Je préfère une stratégie mixte: remplacement du cœur, élimination des composants manifestement modifiés, et reconstruction des plugins et thèmes.
Le cœur WordPress: si wp-admin, wp-includes, wp-content (hors uploads) ou wp-config.php sont touchés, le plus sûr est de réinstaller depuis une archive saine. Vous pouvez garder wp-config.php si vous êtes certain qu’il n’a pas été modifié de manière malveillante, mais dans le doute, comparez et reconstruisez en utilisant vos paramètres réels (base de données, clés de sécurité, etc.).
Wp-content: c’est la zone la plus délicate. Uploads peut contenir des fichiers légitimes, mais aussi des exécutables déguisés. Le malware aime aussi cacher un script dans un sous-dossier discret. Plutôt que de supprimer au hasard, je procède par comparaison et par logique d’usage. Si un fichier PHP apparaît dans un dossier uploads, ou si un fichier “image” a un contenu PHP reconnaissable, c’est un signal clair. En revanche, supprimer massivement uploads peut détruire des médias essentiels.

Plugins et thèmes: si un plugin ou thème est modifié, vous avez deux options réalistes. Soit vous remplacez la version par celle que vous aviez en origine (ou une version saine proche), soit vous désactivez et supprimez puis testez. Dans un incident réel, je privilégie la reconstruction, parce que même si vous supprimez le bout de code visible, une autre branche peut rester.
Cas limite: des modifications légitimes. Par exemple, un thème enfant a des fonctions personnalisées. Là, une approche en “diff” est plus sûre. Comparez vos fichiers modifiés avec la version du thème enfant attendue, ou avec une version contrôlée par votre dépôt (si vous utilisez Git). Si vous ne disposez pas de version de référence, la décision devient plus risquée, et vous devrez tester plus finement.
Nettoyer la base de données sans effacer ce qui doit rester
Après les fichiers, la base de données est souvent le deuxième théâtre. Les infections peuvent ajouter des entrées dans wp_options, modifier des meta de posts, créer des utilisateurs, ou injecter du code via des champs.
Les actions typiques consistent à:
- Identifier les utilisateurs administrateurs récemment créés ou dont le rôle a changé. Vérifier wp_options: chercher des valeurs inhabituelles, des patterns de code, des clés d’options liées à la redirection ou à l’exécution de scripts. Le malware peut stocker des URL et des paramètres. Examiner wp_cron et les schedules si vous avez accès, pour repérer des déclencheurs non désirés. Chercher dans les contenus (pages, brouillons, stylesheets stockés, shortcodes) des marqueurs de scripts.
Je fais attention à un point: les migrations et les plugins légitimes peuvent modifier wp_options. Si vous supprimez des entrées “au feeling”, vous pouvez casser un élément fonctionnel. Le bon réflexe est de croiser: une entrée suspecte doit correspondre à un symptôme. Par exemple, une option contenant une URL de redirection dont vous voyez le comportement dans le front.
Vous n’avez pas forcément besoin de “nettoyer toute la base”. Souvent, vous avez besoin d’enlever quelques modifications ciblées. Mais il faut faire le tri proprement.
Vérifier la présence d’un code latent, pas seulement les traces visibles
Le code latent est la raison pour laquelle un site “a l’air propre” mais continue de https://gardewp.fr/nettoyage-malware-wordpress/ mal se comporter. Il peut rester dans:
- un fichier PHP inconnu que vous n’aviez pas exécuté pendant vos tests initiaux, un plugin inactif mais laissé dans le dossier, un hook WordPress déclenché seulement dans certaines conditions, un cron injecté qui tourne périodiquement, un script côté serveur, ou une modification de configuration.
Un exemple concret: j’ai déjà vu un code qui se déclenchait uniquement si un paramètre de requête était présent, ou seulement pour certains agents user. Pendant les tests classiques, on ne le voyait pas, puis un crawler le rencontrait, déclenchait la redirection, et les dégâts apparaissaient ailleurs.
Pour détecter ce genre de latent, je recommande des vérifications à la fois “statique” et “dynamiques”.
Sur le statique, faites des recherches dans les fichiers sur des motifs fréquents. Par exemple, chaînes qui indiquent une exécution variable, chargement de contenu depuis des URL externes, usage de fonctions “dangereuses” (dans le sens large), ou obfuscation. Les signatures varient selon les familles, mais l’idée reste: repérer des incohérences dans votre code source.
Sur le dynamique, surveillez. Si votre site reste “proche du normal” en apparence, mais que les performances et les logs montrent des comportements irrationnels, c’est un indice que quelque chose continue d’agir en arrière-plan.
Vérifier les performances après nettoyage, comme preuve indirecte
C’est ici que beaucoup de nettoyages échouent. Le site revient en ligne, et on considère le problème réglé. Pourtant, un code latent peut continuer à ralentir la génération de pages, à déclencher des appels réseau externes, ou à faire travailler le serveur sur des tâches inutiles.
Je fais une vérification en trois couches: métriques client, métriques serveur, et comportement applicatif.
1) Côté client: latence et pages qui “louchent”
Sur l’observabilité côté navigateur, regardez au moins:
- temps de chargement global, temps du premier octet (TTFB), taille et nombre de requêtes, éventuels scripts supplémentaires qui ne devraient pas être là.
Si vous observez des différences significatives par rapport à votre baseline, c’est un drapeau. Une infection peut injecter du JavaScript, mais elle peut aussi déclencher des appels au serveur, ou altérer le cache, ce qui augmente les temps de réponse.

En pratique, je compare plusieurs pages, pas uniquement la page d’accueil. Contact, recherche, page produit, page catégories, et pages administratives si vous y avez accès. Les injecteurs ciblent souvent des zones précises.
2) Côté serveur: erreurs, CPU, pics mémoire
Regardez les métriques serveur disponibles via votre hébergement ou votre monitoring:
- CPU sur de courtes périodes, pic de mémoire PHP, augmentation d’erreurs 500 ou 403, temps CPU par requête si votre stack le permet.
Une fois, j’ai nettoyé un site et j’ai vu que la charge CPU restait élevée pendant des créneaux réguliers. Les pages “normales” semblaient OK, mais le serveur chauffait. Dans les logs, on voyait un motif récurrent de requêtes vers un chemin qui n’existait pas. Le code latent appelait une route non standard, qui déclenchait des erreurs et de la charge. Une fois la source retrouvée, la CPU est redevenue stable.
3) Côté applicatif: requêtes PHP, logs et cache
WordPress a un comportement qui varie selon le cache. Si vous utilisez un plugin de cache (ou un proxy comme Varnish, Cloudflare, ou un CDN), un problème de cache peut masquer le diagnostic ou au contraire révéler le problème.
Après nettoyage:
- videz et reconstruisez les caches, testez avec une session “propre” et une session “connectée”, testez en mode navigation privée pour éviter un cache navigateur.
Je recommande de vérifier aussi les logs PHP, ou au minimum les erreurs WordPress. Parfois, le malware produit des erreurs silencieuses: un include inexistant, une URL externe inaccessible, ou un fopen sur un chemin interdit. Ce n’est pas toujours visible côté front, mais c’est visible en log.
Vérifier l’absence de code latent: techniques qui marchent vraiment
Il existe des approches automatisées, mais je privilégie celles qui réduisent le faux positif. Un scan peut vous dire “vous avez trouvé du suspect”, mais vous devez comprendre si c’est une vraie menace ou un faux match.
Sans entrer dans une liste trop longue, voici comment je mène la vérification d’absence de code latent:
D’abord, je définis une fenêtre temporelle. Si le site était compromis pendant une période connue, je compare les logs avant et après. Si vous voyez les mêmes patterns de requêtes continuant après le nettoyage, vous avez un indice fort.
Ensuite, je contrôle les fichiers via comparaison d’origines. Pour le cœur WordPress, la comparaison est simple. Pour thèmes et plugins, la comparaison dépend de votre capacité à retrouver une version saine.
Puis, je fais une chasse ciblée dans wp-content. Là où le malware aime se cacher, c’est dans les endroits qui ne changent pas souvent: plugins inactifs, fichiers abandonnés, et certains dossiers cachés. Même si vous n’utilisez pas un plugin, s’il reste implanté, il peut redevenir actif via une action externe.
Dernier point, souvent négligé: la persistance via permissions. Si un malware a modifié les permissions ou ajouté des écritures possibles dans des dossiers sensibles, vous pouvez supprimer le code, puis vous retrouver avec une réinjection dès que quelqu’un (ou quelque chose) a le même niveau de droits.
Durcir et sécuriser après nettoyage: empêcher la réinfection
Nettoyer ne suffit pas. Une infection est presque toujours le symptôme d’une faiblesse ailleurs: identifiants réutilisés, accès faible, plugins trop vieux, mises à jour ignorées, ou règles serveur trop permissives.
Le meilleur moment pour durcir, c’est juste après avoir restauré un état propre. Sinon, vous perdez le contexte et vous repartez “à froid”.
Voici ce que je fais systématiquement.
Je commence par les identifiants. Déconnectez les sessions si l’option est disponible, changez les mots de passe de tous les comptes ayant des rôles élevés, et supprimez les comptes inutiles. Si vous avez des accès SFTP/SSH, changez aussi les mots de passe et limitez les comptes.
Ensuite, je mets à jour les composants essentiels. Le cœur WordPress, les plugins, et le thème. Si un plugin “offline” est une cause fréquente, je le remplace ou je le retire.
Puis, je vérifie les fichiers critiques. Le fichier wp-config.php doit être sain, et les clés de sécurité doivent être cohérentes. Je vérifie aussi .htaccess ou la configuration Nginx si je peux, pour m’assurer que rien n’a été modifié de manière inattendue.
Enfin, je place des garde-fous côté serveur: limitation des tentatives de connexion, règles de filtrage du bruteforce, et logs suffisamment détaillés pour repérer la prochaine vague.
Mini-plan “à la fin” pour ne pas rater l’essentiel
- Remplacer le mot de passe des comptes admin et supprimer les comptes douteux Réinstaller proprement les thèmes et plugins modifiés, pas juste “nettoyer un morceau” Vérifier wp-config.php, .htaccess et les règles serveur Mettre à jour WordPress, thèmes et plugins, puis tester la compatibilité Surveiller les logs et les métriques de performance pendant au moins 24 à 72 heures
Oui, c’est un délai. Une infection peut redevenir active après une réinitialisation ou un cycle planifié.
Une méthode de contrôle sur 72 heures, réaliste et utile
Si vous êtes pressé, vous faites un check rapide puis vous passez à autre chose. Je recommande une fenêtre de contrôle un peu plus longue. Pas parce que la menace va forcément revenir, mais parce que le malware sait parfois se déclencher plus tard.
Pendant 72 heures après le nettoyage, surveillez:
- les erreurs 403 et 404 répétitives sur des chemins inhabituels, les pics CPU ou temps de réponse anormal, la création de nouveaux utilisateurs, les modifications de fichiers sur wp-content, et la cohérence du HTML rendu (sans injection).
Le but n’est pas d’obséder. Le but est de détecter un comportement qui persiste.
J’ai déjà vu un cas où tout semblait stable pendant une journée, puis une heure précise a relancé des appels externes. Ce n’était pas visible dans l’inspection statique, car le déclencheur était conditionnel. Les logs, eux, ont tranché.
Comment tester “l’absence” sans avoir une preuve absolue
Chercher l’absence totale de code latent est plus une démarche de réduction de risque qu’une certitude mathématique. On ne peut pas garantir à 100 pourcent qu’un code invisible n’existe pas, surtout si votre environnement est déjà en partie compromis.
Mais vous pouvez établir une probabilité très forte:
- En remplaçant le cœur WordPress par une version saine. En reconstruisant thèmes et plugins potentiellement affectés. En supprimant ou remplaçant ce qui a été modifié sans raison. En vérifiant la base de données sur les zones typiques d’injection. En observant les performances et les logs après nettoyage.
Une infection qui reste active se trahit souvent par des comportements: requêtes inhabituelles, lenteurs, erreurs, ou persistance d’accès.
Choisir entre “nettoyer” et “reconstruire” quand tout est trop flou
Il y a un moment où le nettoyage à la carte devient un exercice de foi. Si vous avez un site très critique, si vous n’avez pas de sauvegardes fiables, ou si vous voyez des modifications massives, la reconstruction est parfois plus rationnelle.
Reconstruire veut dire repartir d’une base saine, réimporter uniquement:
- votre contenu (posts, pages), vos médias validés, vos configurations légitimes, et vos thèmes et plugins dans des versions vérifiées.
Le trade-off est clair: c’est plus long, mais plus propre. Sur un site vitrine, on peut tenter de nettoyer au détail. Sur un site e-commerce, où la performance et la confiance sont cruciales, je préfère souvent une reconstruction, parce que le coût de l’erreur est plus élevé.
Garder une trace pour vos prochains audits
Après un incident, la tentation est de repartir comme avant. Je vous conseille plutôt de documenter ce que vous avez trouvé, et ce que vous avez changé.
Sans tomber dans une surcharge, notez:
- les chemins ou fichiers concernés, les plugins ou thèmes qui ont été remplacés, les comptes supprimés ou restaurés, et les raisons probables de l’intrusion (si vous les avez identifiées).
Cette trace devient votre base pour “nettoyer site WordPress infecté” la prochaine fois plus vite, avec moins d’improvisation. Et elle aide aussi quand vous devez convaincre un client ou une équipe: vous montrez que l’intervention n’était pas un bricolage.
Recommandations finales pour garder un site stable après réparation
Le vrai indicateur de réussite, ce n’est pas seulement “le site charge”. C’est “le site charge de façon stable, avec des temps de réponse raisonnables, et sans comportements suspects dans les logs”.
Si les performances restent instables, même après nettoyage, ne minimisez pas. Cela peut être un code latent, ou un effet indirect, par exemple un cache cassé, une configuration serveur modifiée, ou une extension qui redémarre en boucle.
Et gardez une règle simple, que j’ai apprise en répétant les incidents: tant que vous n’avez pas la certitude sur les fichiers et les déclencheurs de persistance, ne considérez pas l’affaire classée.
Un incident WordPress est parfois visible en surface. Mais un bon nettoyage, c’est un nettoyage qui tient dans le temps, avec des performances redevenues cohérentes et une absence de code latent démontrée par l’observation, pas uniquement par un scan.