5 min

Protéger un formulaire du spam sans Captcha : d'Akismet à mon anti-spam maison

2026-05-06

En novembre 2016, je publiais sur mon blog WordPress un billet intitulé « Comment paramétrer Akismet ? ». J'y expliquais comment activer le module anti-spam livré avec WordPress, récupérer une clé API sur le site officiel, puis choisir entre la suppression directe des commentaires indésirables et une boîte de probation de quatorze jours. Je partageais même un petit filtre à coller dans functions.php pour porter ce délai à trente jours, avec cette phrase qui résume déjà tout : « nous ne sommes jamais à l'abri d'un faux-positif ». Dix ans plus tard, je ne protège plus des commentaires de blog mais les formulaires de mon propre site, construit sur Pulsar, mon framework PHP. Et ce réflexe de 2016, préférer garder un message douteux plutôt que d'en perdre un bon, est devenu la règle centrale de l'anti-spam que j'ai fini par écrire moi-même.

Ce qu'Akismet m'avait appris

À l'époque, je déléguais tout. Akismet combinait un dictionnaire de mots-clés indésirables, des règles et une liste noire, et classait les commentaires en attente ou en indésirable. Pour un blog, c'était efficace et je le recommandais sans réserve.

Mais déléguer a un prix. Chaque message part vers des serveurs tiers pour analyse, ce qui, sur un formulaire de contact où un prospect laisse son nom et son email, est devenu un vrai sujet RGPD. Et surtout, le coût d'un faux positif a changé d'échelle : un commentaire de blog perdu, c'est dommage. Une demande de devis perdue, c'est un client qui ne reviendra pas. Mon ancien site a fini par crouler sous le spam de formulaire de contact, et c'est en repensant le nouveau que j'ai posé une contrainte de départ : filtrer sérieusement, sans jamais faire payer la facture aux humains.

Pourquoi pas de Captcha visible

La réponse habituelle au spam, c'est un Captcha. Je l'ai écartée pour trois raisons.

La friction, d'abord. Chaque étape ajoutée avant l'envoi fait abandonner des visiteurs réels, pendant que les robots sérieux résolvent ces puzzles ou les font résoudre par des fermes de clics. On perd des humains pour freiner des machines qui ne sont plus freinées.

L'accessibilité, ensuite. Identifier des feux de signalisation dans des vignettes floues exclut les personnes malvoyantes, et les alternatives audio sont pénibles pour tout le monde.

La vie privée, enfin. Les Captcha des grandes plateformes chargent des scripts tiers, observent le comportement du visiteur et posent des questions de consentement que je ne voulais pas avoir à gérer. Mon site n'appelle aucun service externe pour ses formulaires : tout est auto-hébergé, rien ne sort.

Le Captcha punit l'humain pour un problème créé par des robots. Je voulais l'inverse : des vérifications que seuls les robots remarquent.

Cinq couches invisibles plutôt qu'un mur

Aucune vérification n'est parfaite seule. Empilées, elles arrêtent l'essentiel du spam automatisé sans que le visiteur ne voie quoi que ce soit. Je reste volontairement au niveau du concept : publier les noms de champs ou les seuils exacts reviendrait à fournir le mode d'emploi du contournement.

Le pot de miel. Un champ invisible pour les humains, que les robots remplissent parce qu'ils remplissent tout. Détail qui compte : le site répond « message envoyé » au robot sans rien envoyer. Il ne sait pas qu'il a été détecté, donc il n'apprend rien.

Le piège temporel. Au rendu du formulaire, le serveur émet un horodatage signé cryptographiquement. Un humain met des secondes, souvent des minutes, à lire et remplir un formulaire. Un robot poste presque instantanément. La signature rend le jeton infalsifiable, et la couche ne bloque que sur preuve positive d'automatisation : un jeton absent ou abîmé ne bloque jamais, les autres couches couvrent ce cas. Elle fonctionne sans JavaScript.

Le challenge invisible. Un petit calcul cryptographique (du proof of work) que le navigateur résout tout seul en arrière-plan, en une fraction de seconde, pendant que le visiteur écrit. Imperceptible pour un humain, coûteux pour un robot qui poste en masse. Auto-hébergé, sans clé d'API ni service tiers. Et si le navigateur n'exécute pas JavaScript, l'absence de réponse ne bloque pas l'envoi : les couches côté serveur prennent le relais. La dégradation gracieuse s'applique aussi à la sécurité.

La limitation de débit. Un robot qui inonde le formulaire depuis la même adresse est freiné après quelques tentatives. L'adresse IP est hachée avant tout stockage, je ne la conserve pas en clair.

Le scoring de contenu. Densité de liens, qualité du texte, détection de doublons. Chaque signal ajoute des points à un score, et c'est ici que la philosophie diverge des filtres classiques : ce score ne rejette jamais.

La règle du zéro lead perdu

Mon pipeline distingue deux familles de vérifications. Les preuves positives d'automatisation, comme le pot de miel ou le piège temporel, rejettent. Les signaux de contenu, eux, ne rejettent jamais : un message au score élevé est livré quand même, simplement marqué pour relecture manuelle dans ma boîte.

Un vrai client qui colle trois liens vers son site existant, ou qui écrit deux lignes sèches depuis son téléphone, passe. Je préfère lire deux spams que perdre une demande. C'est exactement la logique de mes trente jours de probation Akismet en 2016, poussée jusqu'au bout : le doute profite toujours au message.

Ce que ça donne en pratique

Sur la page contact comme sur le calculateur de devis, le visiteur ne voit rien de tout cela. Pas de case « je ne suis pas un robot », pas de puzzle, pas de bannière de consentement pour un script tiers. Le formulaire se remplit et s'envoie, point.

Si votre propre formulaire croule sous le spam, ma suggestion tient en une phrase : avant d'ajouter un Captcha qui fera fuir vos visiteurs, empilez des vérifications invisibles et réservez le rejet aux preuves d'automatisation. En 2016, je terminais mon billet en vous invitant à commenter et partager. En 2026, la version honnête est plus simple : si le sujet vous parle, écrivez-moi via ce fameux formulaire. Il tiendra le choc.