Quand un site WordPress devient “bizarre”, on pense tout de suite au front: redirections, pages qui n’existent pas, contenus injectés, formulaires qui changent, ou accès admin qui semble ouvert à n’importe qui. Pourtant, dans beaucoup de cas, le vrai chantier se joue derrière le rideau, dans la base de données. Et c’est là que l’expression “nettoyer site WordPress infecté” prend tout son sens, parce qu’une infection ne se limite pas à un fichier. Elle peut aussi laisser des traces dans les tables, les options, les métadonnées, les tables relationnelles et même des restes d’anciens scripts.
J’ai vu des sites où le ménage des fichiers ne suffisait pas. Tout semblait propre, le cache était vidé, le thème et les plugins étaient remis en ordre, mais le site continuait d’ajouter des entrées en base, ou de déclencher des comportements en cascade dès qu’un utilisateur se connectait. Dans ce type de situation, nettoyer les tables inutiles et corriger la base devient une étape centrale, pas un “plus”.
Le piège des “fichiers propres” et de la base qui continue d’agir
WordPress lit en permanence des informations stockées dans la base. Les options, les cron, les rôles, les meta, les transients, certaines tables d’extensions, ou encore des données de sécurité peuvent contenir des valeurs qui déclenchent des actions au prochain chargement de page ou au prochain passage du cron.
Un exemple concret: après une compromission via un plugin obsolète, le propriétaire avait remplacé les fichiers WordPress et le plugin suspect. Les symptômes côté front se sont calmés pendant quelques heures, puis ils ont repris. Le responsable technique a fini par constater des lignes “orphelines” dans des tables, associées à un traitement récurrent: une tâche planifiée corrompue, une option qui réinjectait un script, ou une entrée dans une table de gestion d’events. Tant que ces données n’étaient pas corrigées, le site “réapprenait” le problème.
Ce n’est pas un raisonnement paranoïaque. La base peut contenir des points d’appui très simples: un option_value modifié, une entrée active dans une table d’actions, un transient qui reste accroché, ou un meta utilisateur qui n’aurait plus dû exister.
Commencer par ce qui évite de casser WordPress: sauvegarde, verrouillage et cartographie
Avant de toucher aux tables, je conseille toujours une approche en trois temps: sauvegarde, réduction des changements, observation rapide.
Le minimum que j’applique systématiquement:
- Sauvegarde complète de la base (dump SQL) et, si possible, copie des répertoires pertinents. Mise en mode maintenance ou blocage d’accès au front, le temps de corriger, surtout si le site est public et attire des bots. Accès en lecture aux éléments avant modification, histoire de pouvoir comparer “avant/après”.
Ensuite, je “cartographie” ce qui existe. Dans cPanel ou un outil équivalent, on voit le préfixe des tables, on peut lister leur nom, repérer les tables de plugins connus, et anticiper ce qui est normal chez ce site.
Une base WordPress “vivante” comporte presque toujours:
- des tables core (wp options, wpposts, wp postmeta, wpusers, wp usermeta, wpcomments, etc.), des tables de plugins (SEO, cache, formulaires, sauvegardes, sécurité), des tables de thème ou d’extensions spécifiques.
Le point crucial, c’est que “inutiles” ne veut pas dire “supprimables”. Une table peut sembler “rare” mais être utilisée par un plugin indispensable. À l’inverse, des tables ou des entrées peuvent être devenues inutiles car le plugin a été désinstallé, mais les données sont restées. C’est précisément ce genre de reliquat qui cause des comportements inattendus après une compromission.
Identifier ce qui est normal, et ce qui sent la trace
Nettoyer une base sans investigation, c’est comme réparer une fuite en serrant les raccords sans savoir où elle vient. On peut aggraver la situation.
À ce stade, je cherche généralement des signaux concrets:
- des options avec des valeurs inattendues, surtout si elles contiennent des portions de code ou des URLs bizarres; des données dans des tables de planification ou de traitement (cron côté base, tables d’events selon plugins); des utilisateurs créés ou modifiés avec des rôles incohérents, ou des meta utilisateur contenant des marqueurs; des transients qui reviennent en boucle; des entrées de post meta et options qui renvoient vers des domaines externes non liés au projet.
Sans inventer de “méthode magique”, on peut déjà faire beaucoup avec une lecture ciblée et quelques requêtes. L’objectif n’est pas d’effacer tout ce qui ressemble à “suspect”, mais de comprendre ce qui déclenche quoi.
Le cas typique des options et transients
Les tables wp options et wpusermeta sont souvent le théâtre des modifications. Les wp_options contiennent des paires clé-valeur. Une infection peut y écrire une option qui active une redirection, stocke un script, ou déclenche un comportement conditionnel.
Les transients, eux, sont des options temporaires, parfois utilisées par des plugins pour stocker des résultats. Une infection peut s’en servir pour déclencher des mécanismes sans tout reconfigurer. Une fois qu’on supprime les fichiers malveillants, le site doit cesser de “reconstruire” le scénario via ces entrées. Si des transients continuent d’être recréés, c’est un indicateur fort que quelque chose est encore actif.
Je n’essaie pas de tout supprimer d’un bloc, surtout sur des sites qui reposent sur des caches ou des mécanismes de performance. Je préfère cibler les clés avec des motifs liés au plugin de sécurité, à un module de redirection, ou à une famille d’options qui a changé juste au moment de l’incident.
Nettoyer les tables inutiles sans détruire l’écosystème WordPress
Le nettoyage “tables inutiles” peut prendre plusieurs formes, et le bon choix dépend du contexte.
Il y a d’un côté les tables orphelines, souvent créées par un plugin temporaire puis laissées après désinstallation. On les reconnaît parce que leur nom évoque un plugin qu’on sait ne plus utiliser. De l’autre côté, il y a des tables core ou plugin qui sont encore nécessaires, mais qui contiennent des données obsolètes, des entrées vides, des lignes répétées, ou des métadonnées inutiles.
Le plus fréquent, ce n’est pas une suppression de table entière. C’est:
- nettoyer des entrées obsolètes, supprimer des lignes en doublon ou incohérentes, corriger des relations cassées (par exemple des postmeta qui pointent vers des posts absents), nettoyer des logs internes ou des caches stockés en base.
La tentation dangereuse: DROP TABLE
On la voit partout: “supprime les tables inutiles”. Techniquement, c’est possible. Pratiquement, c’est rarement une bonne idée au premier passage, surtout si on ne sait pas exactement le rôle de chaque table.
Un cas réel: un site e-commerce utilisait un plugin de facturation. La base contenait une table qui, au premier coup d’œil, semblait “évènementielle” et donc “non essentielle”. Le propriétaire a voulu nettoyer large, table par table. Résultat, certaines pages ont continué à s’afficher, mais la cohérence des historiques de commande s’est mise à diverger. Le problème n’était pas immédiat, il s’est manifesté à la prochaine synchronisation.
Le meilleur réflexe est de supprimer ce qui est clairement orphelin ou clairement malveillant, et de garder le reste. Si une table n’est plus utilisée, je préfère d’abord vérifier qu’aucun plugin actif ne la référence. Ce détail évite beaucoup de dégâts.
Corriger la base: quand “réparer” veut dire “restaurer la cohérence”
En cas de compromission, “corriger la base” peut vouloir dire plusieurs choses. Ce n’est pas forcément un optimiseur. Parfois, c’est de la cohérence relationnelle et de la validité des données.
Les symptômes qui m’amènent à corriger la base plutôt qu’à juste supprimer des options sont souvent:
- des posts ou pages qui reviennent malgré suppression, des utilisateurs ou rôles qui semblent réapparaître, des données d’autorisations qui ne correspondent plus aux attentes, des entêtes de sécurité qui changent, ou des plugins dont les configurations “redeviennent” celles d’avant.
Vérifier la cohérence posts et métadonnées
WordPress utilise une structure où les posts sont dans une table, et les métadonnées associées dans une table de postmeta. Si l’infection a injecté des entrées de postmeta liées à des posts qui n’existent plus, on peut avoir des comportements résiduels. À l’inverse, si des posts existent mais sont invisibles, on peut avoir des pages “draft” ou “private” qui continuent d’alimenter des redirections.
Sans entrer dans une mécanique trop “requête par requête” pour un article généraliste, je recommande une démarche de vérification:
- retrouver les posts récemment modifiés autour du moment de l’incident; repérer les statuts anormaux; contrôler les meta associées, en particulier celles qui portent des URLs externes, des scripts, ou des marqueurs de plugins.
Dans certains cas, la suppression des entrées suspectes dans postmeta suffit, à condition qu’on n’ait plus de mécanisme en place qui les réécrit.
Étudier les cron et les planifications
Un cron WordPress défaillant ou modifié dans la base peut relancer la compromission ou maintenir une persistance. Selon le plugin et la configuration, les événements peuvent être stockés ailleurs que dans les tables core, mais l’idée reste la même: un processus planifié peut exécuter du code en arrière-plan.
Ce que j’observe souvent, c’est une divergence entre ce que l’admin pense avoir désactivé et ce qui existe réellement dans les événements. Si l’infection a ajouté une tâche, la désactivation du plugin peut ne pas suffire, parce que les données planifiées continuent de vivre.
Le nettoyage du cron et des événements nécessite prudence, car certains plugins légitimes utilisent aussi des actions planifiées. Là encore, je préfère cibler ce qui a été ajouté autour de la période de l’incident, ou ce qui mentionne des classes ou des fonctions absentes des plugins actifs.
Corriger sans casser: encadrer le travail avec un protocole simple
Quand on touche à la base, la plupart des erreurs viennent du “on improvise”.
Voici comment je cadre le travail, sans en faire une usine: 1) je fais une hypothèse (par exemple “des options malveillantes déclenchent une redirection”), 2) je confirme par observation (comparaison avant suppression, ou trace de valeurs), 3) je corrige un morceau (suppression ou mise à jour ciblée), 4) je teste immédiatement (navigation front, connexion admin, vérification que les symptômes n’apparaissent plus), 5) je documente ce qui a changé.
Ce protocole est aussi une protection psychologique. On gagne du temps parce qu’on sait quoi tester. Et on limite les retours en arrière.
À ce stade, je m’appuie aussi sur une logique de rollback. Une sauvegarde SQL donne un filet de sécurité, mais le vrai confort vient du fait d’avoir un plan “si ça revient, je restaure et je réessaie autrement”. On réduit la panique.
Exemple d’approche: restaurer et assainir, puis réduire
Je me souviens d’un site vitrine sur lequel les redirections s’activaient uniquement après connexion admin. L’équipe avait déjà nettoyé le thème, remplacé les plugins et vidé le cache. Pourtant, à chaque nouveau login, la base “reprenait” des éléments.
Après analyse, les indices étaient très concrets:
- des entrées d’options modifiées, un utilisateur supplémentaire avec une capacité imprévue, quelques valeurs dans des meta qui contenaient des fragments d’URL étrangères, des transients qui repopulaient presque immédiatement après suppression.
Le traitement a été fait en deux temps. D’abord, on a stoppé ce qui réécrivait les données en bloquant l’exécution des éléments persistants (au niveau configuration et fichiers encore en action). Ensuite, seulement après, on a nettoyé les entrées en base: options ciblées, métadonnées liées aux posts inutiles, et transients de la même famille. Cette séquence a été décisive. Sinon, on aurait supprimé à chaque fois ce que l’infection re-créait.
C’est souvent le “contre-rythme” qui explique pourquoi certains nettoyages échouent: on nettoie la base trop tôt, ou trop tard, sans couper la source.
Repérer des tables inutiles et des données “orphelines” sans agir au hasard
Quand on parle de tables inutiles, je pense plutôt à des “jeux de données” qui n’ont plus d’acteurs actifs.
Dans une base WordPress, c’est fréquent après:
- une désinstallation de plugin, une migration, un changement de thème, un ancien système de statistiques ou de cache qui n’est plus utilisé.
Ces reliquats posent deux problèmes. D’abord, ils alourdissent la base. Ensuite, s’ils contiennent des marqueurs injectés, ils peuvent devenir un lieu de persistance.
Il y a une approche pragmatique, basée sur la comparaison de ce qui est présent avec ce qui est actuellement installé et configuré.
Voici un mini guide de tri, basé sur ce que je vois le plus souvent, sans promesse “universelle”:
- lister les tables dont le nom évoque un plugin non actif, vérifier la liste des plugins actifs et leur documentation interne (noms de tables souvent cohérents), repérer les tables vides ou presque vides, repérer les tables dont les données ont été créées très récemment autour de la date de l’incident, conserver une copie des tables “candidates” avant suppression.
Cette logique limite les erreurs. Elle évite aussi le piège classique: supprimer une table “rare” alors qu’elle sert encore à un plugin caché ou à un module chargé seulement dans certains contextes.
Ajuster l’outil: réparer les tables MySQL, optimiser avec discernement
Selon l’hébergement, MySQL ou MariaDB peuvent avoir des tables “marquées”, ou des index qui ne sont plus performants. Mais il faut être clair: une compromission ne se règle pas par “REPAIR TABLE”.
Ce que je fais quand l’infrastructure le permet:
- lancer une inspection, corriger uniquement ce qui est corrompu au sens base de données, optimiser uniquement après avoir nettoyé la source du problème.
L’ordre compte. Optimiser avant de comprendre ce qui injecte des données, c’est comme nettoyer le plan de travail alors que la machine reste branchée et continue de casser.
Les fonctions de recherche: utiles, mais pas suffisantes
On peut être tenté d’utiliser des recherches globales sur la base pour trouver des chaînes suspectes. Ça aide, mais ça ne suffit pas. Une infection peut stocker des fragments concaténés, ou des valeurs encodées, ou utiliser des variables dynamiques.
Par ailleurs, les plugins légitimes stockent aussi des chemins et des URLs dans des options. Si on efface à l’aveugle, on casse du paramétrage.
Le bon compromis, c’est de faire des recherches pour repérer des zones à inspecter, puis d’examiner ligne par ligne ce qui est modifié. C’est plus lent, mais plus sûr.

Sécurité après nettoyage: éviter que la base redevienne le point d’entrée
Nettoyer la base ne termine pas le travail. Une fois assainie, il faut réduire la probabilité que le site soit re-compromis.
La plupart des récidives viennent de deux choses:
- un vecteur d’accès non corrigé (mauvaise configuration, mot de passe réutilisé, plugin vulnérable), un manque de durcissement, notamment côté utilisateurs, permissions, et mises à jour.
Ce que je recommande généralement après un nettoyage sérieux de la base, c’est une hygiène complète:
- rotation des mots de passe admin et des autres comptes à privilèges, vérification des comptes utilisateurs, suppressions des comptes inutiles, mise à jour de tous les plugins et du thème, contrôle des fichiers, du chargement des scripts, et des tâches planifiées.
Je le formule autrement, parce que c’est ce qui fait la différence en exploitation: il faut que la base soit “coupée” des mécanismes qui y écrivent de nouveau. Si la source n’est pas corrigée, la base redeviendra sale, même après un nettoyage méticuleux.

Le point qui surprend: tables et performance, sans sacrifier la stabilité
Beaucoup de clients veulent aussi “nettoyer pour que ça aille plus vite”. C’est normal. Mais en post-compromission, on priorise la stabilité, ensuite la performance.
Supprimer des données en base peut accélérer, oui. Mais si c’est fait au mauvais endroit, vous pouvez casser des mécanismes de cache ou perdre des contenus internes. WordPress dépend de certaines tables même si, sur le front, on ne voit rien.
Un compromis que j’ai adopté plusieurs fois:
- d’abord, supprimer les entrées manifestement anormales ou clairement associées à la compromission, ensuite, réduire les tables de cache uniquement après validation que le site fonctionne, enfin, lancer un nettoyage plus “performance” sur une plage plus large si l’équipe le souhaite, avec sauvegarde et tests.
Checklist opérationnelle après un incident (celle que j’utiliserais demain)
Voici une liste courte, centrée sur l’action, pour cadrer un incident de compromission WordPress où la base a aussi été touchée:
Faire une sauvegarde SQL complète et une copie des fichiers, même si vous pensez que c’est “trop tard”. Identifier le moment probable de l’incident, puis repérer les modifications récentes dans les tables options, users, postmeta et tables de plugins. Couper le mécanisme de persistance (fichiers, plugins, accès), puis seulement après nettoyer les entrées en base. Supprimer ou corriger les valeurs clairement liées à la compromission, pas “au feeling”. Tester immédiatement front et back, puis surveiller la base sur quelques heures pour voir si les valeurs reviennent.Quand il faut arrêter et demander un diagnostic plus profond
Il y a des situations où je ne recommande pas de “nettoyer site WordPress infecté” manuellement en solo. Pas parce que c’est impossible, mais parce que la complexité dépasse le gain.
J’envisage un diagnostic plus profond si:
- l’infection revient en moins de quelques minutes après nettoyage, des utilisateurs ou options changent sans action humaine, plusieurs domaines externes apparaissent de façon répétée, des logs hébergeur indiquent des exécutions côté serveur qui ne correspondent à rien dans les fichiers actuels.
Dans ces cas, la persistance peut être très “basse” (accès système, scripts temporaires, tâches au niveau de l’hébergement). La base peut être un miroir du problème, pas la cause unique.
Ce que signifie vraiment “corriger la base” dans un contexte de compromis
“Corriger la base” ne veut pas dire “faire un grand ménage”. Ça veut dire restaurer un état cohérent. Un état cohérent, c’est:
- des options qui correspondent à votre configuration réelle, des utilisateurs dont les rôles sont ceux attendus, des posts et métadonnées qui ne déclenchent pas de redirections ou d’injections, des tables et données de plugins cohérentes avec ce qui est installé et utilisé.
Et, très important, un état cohérent inclut la capacité de WordPress à continuer d’écrire correctement après votre nettoyage. Si vous avez supprimé des choses “au hasard”, WordPress peut se mettre à re-créer des valeurs erratiques. C’est une des raisons pour lesquelles certaines bases “re-saignent” après coup.
En exploitation, la qualité du nettoyage se juge aussi à la stabilité après les corrections. Si tout re-fonctionne pendant plusieurs cycles de navigation et de cron, vous avez probablement fait les bons choix.
Une dernière nuance: le nettoyage n’est pas que technique
On touche à la base, mais la décision de ce qu’on supprime relève aussi d’un jugement sur le projet.
Par exemple, un plugin peut stocker des logs dans la base. Ces logs peuvent contenir des URLs et donc attirer l’œil. Mais ce n’est pas forcément malveillant. Si vous supprimez ces logs parce qu’ils “semblent suspects”, vous pouvez perdre de la visibilité. Pire, vous pouvez désactiver des mécanismes de monitoring qui vous auraient aidé à détecter la compromission plus tôt.
C’est pour ça que je privilégie une logique de preuve. Vous ne nettoyez pas une table parce qu’elle “fait peur”. Vous la nettoyez parce que vous savez pourquoi elle est anormale.
Si vous me donnez le type d’hébergement (cPanel, VPS, docker), le préfixe des tables, et les plugins actifs, je peux vous proposer une méthode encore plus concrète pour repérer les tables candidates et distinguer nettoyage légitime de réparation risquée, sans partir dans des suppressions irréversibles.