Wpoison fait partie de ces vieux outils du web qui sentent bon le CGI, les scripts Perl et les robots spammeurs un peu trop naïfs. Le principe reste pourtant malin : créer de fausses pages remplies d’adresses e-mail bidon pour empoisonner les bases de données des spammeurs.
En revanche, installer un vieux script PHP trouvé dans un dépôt oublié, puis le laisser tourner sans limite sur un serveur moderne, ce n’est plus une bonne idée. Les robots ont changé, les moteurs de recherche aussi, et votre serveur mérite mieux qu’un piège infini bricolé au fond d’un répertoire /cgi-bin/.
Voici donc une approche plus propre : comprendre le principe de wpoison, puis créer un honeypot PHP simple, limité, non indexable et surveillable.
Faut-il encore installer wpoison aujourd’hui ?
Oui, mais pas comme en 2004.
Le script original wpoison avait un objectif très clair : attirer les robots collecteurs d’adresses e-mail, leur servir des pages artificielles, puis les envoyer vers d’autres pages similaires. Résultat attendu : le robot avale des centaines d’adresses inutiles, pollue sa liste, perd du temps, et repart avec une indigestion numérique.
Le concept reste utile pour comprendre les robots spammeurs. Cependant, il faut éviter trois erreurs classiques :
- générer des boucles infinies qui consomment inutilement du CPU ;
- créer de fausses adresses sur de vrais domaines, ce qui peut produire du backscatter ou gêner d’autres administrateurs ;
- laisser indexer ces pages, ce qui dilue la qualité globale du site.
La version moderne doit donc être volontairement limitée. Elle doit attirer les mauvais robots, mais elle ne doit pas nuire au crawl des moteurs légitimes, ni devenir un mini-DDoS auto-infligé. Le piège doit rester un piège, pas une usine à fumée.
Ce que nous allons créer
Nous allons créer un petit script PHP autonome qui :
- génère une page HTML factice ;
- affiche de fausses adresses e-mail sous le domaine réservé
.invalid; - crée quelques liens internes vers d’autres URLs du honeypot ;
- envoie un en-tête
X-Robots-Tag: noindex, nofollow; - applique un rate limit simple par adresse IP ;
- journalise les accès pour repérer les robots trop curieux.
Ce n’est pas une arme magique contre le spam. C’est un leurre discret, utile pour observer et ralentir certains collecteurs automatisés.
Préparer le répertoire du honeypot
Choisissez un chemin isolé, par exemple :
/var/www/skyminds.net/public/honeytrap/Langage du code : PHP (php)
Créez ensuite le répertoire et un sous-répertoire pour les logs :
mkdir -p /var/www/skyminds.net/public/honeytrap/logs
touch /var/www/skyminds.net/public/honeytrap/logs/access.logLangage du code : PHP (php)
Adaptez le propriétaire selon votre stack. Sur un serveur Nginx ou Apache avec PHP-FPM, ce sera souvent www-data.
Marre des agences qui sous-traitent ?
Avec moi, vous parlez directement au développeur qui fait le travail. Pas d'intermédiaire, pas de promesses creuses. Juste du code propre et un interlocuteur joignable.
Travaillons directement ensemble →chown -R www-data:www-data /var/www/skyminds.net/public/honeytrap
find /var/www/skyminds.net/public/honeytrap -type d -exec chmod 750 {} \;
find /var/www/skyminds.net/public/honeytrap -type f -exec chmod 640 {} \;Langage du code : PHP (php)
Le répertoire doit être accessible par PHP, mais il ne doit pas devenir une zone d’écriture publique. On garde les choses propres, sinon le piège se retourne contre vous. Ambiance cartoon, râteau dans la figure.
Le script PHP du honeypot
Créez le fichier suivant :
nano /var/www/skyminds.net/public/honeytrap/index.phpLangage du code : PHP (php)
Collez ce code PHP compatible PHP 8.3+ :
<?php
/**
* Simple PHP honeypot inspired by wpoison.
*
* This script generates fake content and fake email addresses for spam harvesters.
* It also limits requests per IP and sends noindex headers for legitimate crawlers.
*
* @package SkyMinds_Honeytrap
*/
declare(strict_types=1);
const SKY_HONEYTRAP_MAX_LINKS = 8;
const SKY_HONEYTRAP_MAX_EMAILS = 18;
const SKY_HONEYTRAP_RATE_LIMIT = 60;
const SKY_HONEYTRAP_RATE_WINDOW = 3600;
const SKY_HONEYTRAP_LOG_FILE = __DIR__ . '/logs/access.log';
const SKY_HONEYTRAP_RATE_LIMIT_DIR = __DIR__ . '/logs/rate-limit';
/**
* Return the visitor IP address.
*
* Keep this conservative. Do not trust forwarded headers unless your reverse proxy
* sanitises them before the request reaches PHP.
*
* @return string
*/
function sky_honeytrap_get_ip(): string {
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
/**
* Send common response headers.
*
* @return void
*/
function sky_honeytrap_send_headers(): void {
header('Content-Type: text/html; charset=UTF-8');
header('X-Robots-Tag: noindex, nofollow, noarchive', true);
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0', true);
header('Referrer-Policy: no-referrer', true);
header('X-Content-Type-Options: nosniff', true);
}
/**
* Apply a small file-based rate limit per IP address.
*
* @param string $ip Visitor IP address.
* @return bool True when the request may continue.
*/
function sky_honeytrap_allow_request(string $ip): bool {
if (! is_dir(SKY_HONEYTRAP_RATE_LIMIT_DIR)) {
mkdir(SKY_HONEYTRAP_RATE_LIMIT_DIR, 0750, true);
}
$key = hash('sha256', $ip);
$file = SKY_HONEYTRAP_RATE_LIMIT_DIR . '/' . $key . '.json';
$now = time();
$data = [
'window_start' => $now,
'count' => 0,
];
if (is_readable($file)) {
$decoded = json_decode((string) file_get_contents($file), true);
if (is_array($decoded)) {
$data = array_merge($data, $decoded);
}
}
if (($now - (int) $data['window_start']) > SKY_HONEYTRAP_RATE_WINDOW) {
$data = [
'window_start' => $now,
'count' => 0,
];
}
$data['count'] = (int) $data['count'] + 1;
file_put_contents(
$file,
json_encode($data, JSON_THROW_ON_ERROR),
LOCK_EX
);
return $data['count'] <= SKY_HONEYTRAP_RATE_LIMIT;
}
/**
* Write a compact access log line.
*
* @param string $ip Visitor IP address.
* @return void
*/
function sky_honeytrap_log_access(string $ip): void {
$user_agent = substr($_SERVER['HTTP_USER_AGENT'] ?? '-', 0, 300);
$path = substr($_SERVER['REQUEST_URI'] ?? '-', 0, 300);
$line = sprintf(
"[%s]\t%s\t%s\t%s\n",
gmdate('c'),
$ip,
str_replace(["\n", "\r", "\t"], ' ', $path),
str_replace(["\n", "\r", "\t"], ' ', $user_agent)
);
file_put_contents(SKY_HONEYTRAP_LOG_FILE, $line, FILE_APPEND | LOCK_EX);
}
/**
* Return a random item from a list.
*
* @param array<int, string> $items List of strings.
* @return string
*/
function sky_honeytrap_pick(array $items): string {
return $items[array_rand($items)];
}
/**
* Generate a fake email address under a reserved invalid domain.
*
* @return string
*/
function sky_honeytrap_generate_email(): string {
$first_names = [
'alice',
'bruno',
'camille',
'diane',
'emile',
'flora',
'gaston',
'helene',
'iris',
'julien',
];
$roles = [
'contact',
'admin',
'editor',
'newsletter',
'support',
'billing',
'press',
'office',
];
$domains = [
'example.invalid',
'mailbox.invalid',
'archive.invalid',
'webmaster.invalid',
'directory.invalid',
];
return sprintf(
'%s.%s%d@%s',
sky_honeytrap_pick($roles),
sky_honeytrap_pick($first_names),
random_int(100, 9999),
sky_honeytrap_pick($domains)
);
}
/**
* Generate a fake internal honeypot link.
*
* @return string
*/
function sky_honeytrap_generate_link(): string {
return sprintf(
'/honeytrap/%s/%d/',
bin2hex(random_bytes(4)),
random_int(1000, 999999)
);
}
/**
* Escape HTML output.
*
* @param string $value Raw value.
* @return string
*/
function sky_honeytrap_escape(string $value): string {
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
$ip = sky_honeytrap_get_ip();
sky_honeytrap_send_headers();
sky_honeytrap_log_access($ip);
if (! sky_honeytrap_allow_request($ip)) {
http_response_code(429);
echo '<!doctype html><html lang="fr"><head><meta charset="utf-8"><meta name="robots" content="noindex,nofollow"><title>Too many requests</title></head><body><p>Too many requests.</p></body></html>';
exit;
}
$title_words = [
'archives',
'directory',
'contacts',
'newsletter',
'public',
'index',
'profiles',
'resources',
];
?><!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex,nofollow,noarchive">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?php echo sky_honeytrap_escape(ucfirst(sky_honeytrap_pick($title_words))); ?></title>
</head>
<body>
<main>
<h1><?php echo sky_honeytrap_escape(ucfirst(sky_honeytrap_pick($title_words))); ?></h1>
<p>Legacy directory snapshot. Automated systems may discover outdated contact records below.</p>
<ul>
<?php for ($i = 0; $i < SKY_HONEYTRAP_MAX_EMAILS; $i++) : ?>
<?php $email = sky_honeytrap_generate_email(); ?>
<li><a href="mailto:<?php echo sky_honeytrap_escape($email); ?>" rel="nofollow"><?php echo sky_honeytrap_escape($email); ?></a></li>
<?php endfor; ?>
</ul>
<nav aria-label="Archive links">
<ul>
<?php for ($i = 0; $i < SKY_HONEYTRAP_MAX_LINKS; $i++) : ?>
<li><a href="<?php echo sky_honeytrap_escape(sky_honeytrap_generate_link()); ?>" rel="nofollow">Archived contact page</a></li>
<?php endfor; ?>
</ul>
</nav>
</main>
</body>
</html>Langage du code : HTML, XML (xml)
Ce script ne génère pas d’adresses sur des domaines réels. Il utilise .invalid, réservé pour les exemples et les tests. C’est moins agressif qu’un wpoison historique, mais c’est plus propre pour un serveur de production.
Réécriture d’URL avec Nginx
Si vous utilisez Nginx, ajoutez une règle dédiée dans le bloc serveur du site :
location ^~ /honeytrap/ {
index index.php;
try_files $uri /honeytrap/index.php?$query_string;
add_header X-Robots-Tag "noindex, nofollow, noarchive" always;
}Langage du code : PHP (php)
Ensuite, vérifiez la configuration et rechargez Nginx :
nginx -t
systemctl reload nginx
Réécriture d’URL avec Apache
Sur Apache, vous pouvez créer un fichier .htaccess dans le répertoire honeytrap :
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]
Header always set X-Robots-Tag "noindex, nofollow, noarchive"Langage du code : JavaScript (javascript)
Assurez-vous que mod_rewrite et mod_headers sont actifs si vous gérez votre propre serveur.
a2enmod rewrite headers
systemctl reload apache2
Faut-il bloquer le honeypot dans robots.txt ?
Pas forcément. C’est le piège classique.
Si vous bloquez /honeytrap/ dans robots.txt, Google et les moteurs respectueux ne verront pas toujours les directives noindex envoyées par la page. Une URL bloquée par robots.txt peut encore apparaître dans les résultats si d’autres pages créent des liens vers elle.
Pour un honeypot que vous ne voulez pas voir indexé, préférez donc :
- un en-tête HTTP
X-Robots-Tag: noindex, nofollow; - une balise
<meta name="robots" content="noindex,nofollow">; - des liens internes avec
rel="nofollow"; - aucun lien depuis vos pages importantes.
En clair : on laisse les moteurs légitimes comprendre que la page ne doit pas être indexée. Les robots spammeurs, eux, feront probablement ce qu’ils veulent. C’est précisément pour eux que le piège existe.
Ajouter un lien discret vers le honeypot
Vous pouvez ajouter un lien discret dans le footer ou dans une page peu visible. Évitez toutefois les liens cachés via CSS agressif, car cela peut ressembler à une technique SEO douteuse. Restez simple.
<a href="/honeytrap/" rel="nofollow" aria-label="Anciennes archives">Archives</a>Langage du code : HTML, XML (xml)
Le lien doit être peu mis en avant, mais il ne doit pas être trompeur pour les moteurs légitimes. L’idée n’est pas de manipuler Google. L’idée est d’offrir une fausse piste aux robots qui aspirent tout sans discernement.
Surveiller les accès au honeypot
Le script écrit les accès dans :
/var/www/skyminds.net/public/honeytrap/logs/access.logLangage du code : PHP (php)
Pour voir les derniers passages :
tail -f /var/www/skyminds.net/public/honeytrap/logs/access.logLangage du code : PHP (php)
Pour compter les user agents les plus fréquents :
awk -F '\t' '{print $4}' /var/www/skyminds.net/public/honeytrap/logs/access.log | sort | uniq -c | sort -nr | head -30Langage du code : PHP (php)
Et pour repérer les IP les plus insistantes :
awk -F '\t' '{print $2}' /var/www/skyminds.net/public/honeytrap/logs/access.log | sort | uniq -c | sort -nr | head -30Langage du code : PHP (php)
Si une IP tape le honeypot en boucle, vous pouvez ensuite la traiter avec Fail2ban, CrowdSec, votre WAF ou les règles de votre reverse proxy. Le honeypot devient alors un signal, pas seulement une blague anti-spam.
Limiter les risques côté serveur
Un honeypot mal conçu peut coûter plus cher à votre serveur qu’au robot. C’est l’erreur à éviter.
Gardez ces règles en tête :
- limitez le nombre de liens générés par page ;
- limitez le nombre d’adresses affichées ;
- bloquez les requêtes trop fréquentes avec un code HTTP
429; - ne déclenchez aucun envoi d’e-mail ;
- n’utilisez pas de vrais domaines dans les fausses adresses ;
- n’ajoutez pas le honeypot dans le sitemap XML ;
- surveillez les logs après mise en ligne.
Le but n’est pas de construire un labyrinthe infini. Le but est de créer une zone d’observation contrôlée. Le spammeur tombe dans le trou, mais votre serveur ne descend pas avec lui.
Et pour WordPress ?
Sur WordPress, je ne recommande pas d’intégrer ce type de script dans le thème ou dans un plugin chargé sur toutes les pages. Cela ajouterait du bruit à une application déjà sollicitée.
Le plus propre consiste à garder le honeypot en script autonome, dans un répertoire isolé. WordPress n’a pas besoin de charger son bootstrap complet pour servir une page bidon à un robot spammeur. Ce serait comme sortir la porcelaine pour nourrir un pigeon.
En revanche, WordPress propose déjà une fonction utile pour afficher une adresse e-mail de manière moins triviale dans un thème ou un plugin : antispambot(). Elle ne bloque pas les bons collecteurs, mais elle évite au moins d’exposer une adresse en clair dans le HTML.
<?php
/**
* Display an obfuscated email address.
*
* @package SkyMinds
*/
$email = antispambot( 'contact@example.com' );
printf(
'<a href="mailto:%1$s">%1$s</a>',
esc_attr( $email )
);
?>Langage du code : HTML, XML (xml)
Pour un formulaire de contact, utilisez plutôt un vrai mécanisme anti-spam : honeypot de champ invisible, jeton nonce, limitation de fréquence, validation serveur et protection côté WAF si nécessaire.
Alternative : protéger les formulaires plutôt que piéger les robots
Wpoison vise surtout les collecteurs d’adresses e-mail. Or, sur les sites modernes, les problèmes viennent souvent davantage des formulaires : spam de commentaires, inscriptions bidon, injections dans les champs, faux comptes clients, paniers WooCommerce pollués, etc.
Dans ce cas, le honeypot de formulaire est généralement plus efficace qu’un piège façon wpoison. Par exemple, vous pouvez ajouter un champ invisible que les humains ne remplissent jamais, mais que certains bots remplissent automatiquement. Si le champ contient une valeur, vous rejetez la soumission.
Ce principe est simple, rapide, et peu intrusif. Il peut aussi être combiné avec une limite par IP, un délai minimal avant soumission, ou une solution comme Cloudflare Turnstile lorsque le spam devient vraiment pénible.
FAQ
Wpoison bloque-t-il vraiment le spam ?
Non, pas directement. Wpoison ne bloque pas les e-mails entrants. Il tente plutôt de polluer les listes collectées par certains robots spammeurs. C’est une technique de nuisance, pas une protection complète.
Peut-on utiliser de vraies adresses e-mail factices ?
Mieux vaut éviter. Une adresse inventée sur un vrai domaine peut provoquer des rebonds, gêner un serveur tiers ou créer du bruit inutile. Utilisez un domaine réservé comme .invalid, ou un domaine que vous contrôlez réellement.
Faut-il ajouter le honeypot au fichier robots.txt ?
Pas si votre priorité est d’éviter l’indexation. Utilisez plutôt X-Robots-Tag: noindex, nofollow et une balise meta robots. Un blocage dans robots.txt peut empêcher les moteurs de voir le noindex.
Le honeypot peut-il ralentir mon site ?
Oui, s’il génère trop de pages ou s’il accepte trop de requêtes. C’est pourquoi le script ci-dessus limite les accès, le nombre de liens et le nombre d’adresses générées.
Est-ce utile contre les robots IA ?
Pas vraiment sous cette forme. Wpoison cible surtout les collecteurs d’e-mails. Pour les robots IA et les scrapers modernes, il vaut mieux combiner logs, WAF, règles serveur, cache, limitation de fréquence et politique claire dans robots.txt.
Conclusion
Wpoison n’est plus la solution anti-spam miracle que l’on pouvait imaginer au début des années 2000. Cependant, son idée reste excellente : les robots automatisés qui collectent sans réfléchir peuvent être envoyés vers des contenus sans valeur.
La bonne approche actuelle consiste à créer un honeypot sobre, limité, non indexable et surveillé. Vous protégez ainsi votre serveur, vous évitez de polluer votre SEO, et vous récupérez des signaux utiles sur les robots qui rôdent autour de votre site.
Bref, gardez l’esprit de wpoison, mais laissez les vieilles pratiques au musée. Elles y sont très bien, entre un modem 56k et un CD AOL.
Besoin d’un site WordPress plus propre, plus rapide et mieux protégé ?
J’aide les entreprises à optimiser, sécuriser et maintenir leurs sites WordPress et WooCommerce : performance, sécurité, développement sur mesure, migrations, nettoyage de plugins, audit serveur et résolution de problèmes techniques coriaces.
Si votre site attire trop de bots, charge trop lentement ou casse dès qu’un plugin tousse, je peux vous aider à remettre de l’ordre proprement.
À lire aussi sur SkyMinds
- Comment empêcher les chatbots IA d’extraire le contenu de votre site
- Gravity Forms : activer l’anti-spam honeypot sur tous les formulaires
- Protéger une newsletter des spammeurs
- Apache : prévenir le vol d’images par hotlinking
Sources
- Wired — Wpoison Sets Trap for Spam Weasel
- Google Search Central — Block Search Indexing with noindex
- WordPress Codex — Protection From Harvesters
Besoin d'un coup de main ?
Ce bug qui traîne depuis des semaines, ce plugin qui casse votre mise en page, cette fonctionnalité que personne n'arrive à implémenter proprement — c'est exactement ce que je règle au quotidien depuis 20 ans.
Parlons de votre problème →