Protéger un formulaire WordPress contre les robots, sans extension
Il n’y a pas d’extension Antisy pour WordPress : l’intégration se fait à la main, dans un plugin à vous ou dans le thème. Une balise dans le <head>, puis la fonction PHP de la documentation sur le crochet qui reçoit le formulaire : la réponse est allow ou deny.
État au .
Existe-t-il une extension WordPress ?
Non. Antisy ne propose pas d’extension WordPress à ce jour, et ce guide n’en annonce pas. Il montre ce que le produit livre aujourd’hui, une balise et un appel serveur, que vous branchez vous-même sur WordPress.
La fonction de vérification est celle de la documentation. Les extraits propres à WordPress (wp_head, pre_comment_on_post, admin_post_*) utilisent des crochets standard ; ils n’ont pas été exécutés dans une installation WordPress à la date de cette page.
Ce qu’il vous faut
- Un site déclaré au tableau de bord d’Antisy, avec les noms d’hôte de vos pages.
- La clé de site (32 caractères) : elle est publique et va dans la page ; le secret de vérification (64 caractères) reste sur votre serveur et n’est montré qu’une fois, à sa création.
- Des pages servies en HTTPS.
- La main sur un plugin à vous ou sur le thème (un thème enfant évite de perdre les modifications à la mise à jour), et sur le fichier
wp-config.php. - PHP 8.0 ou plus avec l’extension cURL chez votre hébergeur : la fonction de la documentation utilise
curl_initet le typemixed.
Est-ce disponible aujourd’hui ?
L’accès se fait sur liste d’attente. L’API et le tableau de bord sont en service depuis le 27 octobre 2026, hébergés chez Infomaniak à Genève ; aucun client n’est encore en production.
Les exemples de ce guide suivent la version 1 de l’API, telle que la documentation la décrit. Pour les essayer sans protéger un vrai formulaire, la documentation décrit des clés de test ; une clé de test ne va jamais en production.
Voir : Documentation : clés de test · Le pilote · Rejoindre la liste d’attente
1. Les deux valeurs
Dans wp-config.php, hors de la racine publique du dépôt du thème. La fonction de la documentation lit le secret avec getenv('ANTISY_SECRET'). Chez un hébergeur qui n’offre pas de variable d’environnement, une seule ligne change :
<?php
// wp-config.php, au-dessus de la ligne « That's all, stop editing! ».
define('ANTISY_SITE_KEY', 'VOTRE_CLE_DE_SITE'); // publique : elle va dans la page
define('ANTISY_SECRET', 'VOTRE_SECRET_DE_VERIFICATION'); // secret : jamais dans le dépôt d'un thème// Dans antisy.php, remplacez cette ligne de la fonction de la documentation :
'secret' => getenv('ANTISY_SECRET'),
// par celle-ci :
'secret' => ANTISY_SECRET,2. La balise dans chaque page
La balise est imprimée par le crochet wp_head, avec la clé de site :
<?php
// Dans un plugin à vous, ou dans functions.php du thème : la balise, dans le <head> de chaque page.
add_action('wp_head', function () {
if (!defined('ANTISY_SITE_KEY')) {
return;
}
printf(
'<script src="https://api.antisy.com/v1/i/l.js" data-site="%s" defer></script>' . "\n",
esc_attr(ANTISY_SITE_KEY)
);
});
// La fonction antisy_verdict() de la documentation, enregistrée dans antisy.php à côté de ce fichier.
require_once __DIR__ . '/antisy.php';Le jeton n’est pas dans le HTML de la page : le script le crée à l’envoi, dans le navigateur. Une page servie par un cache de pages ou par un CDN fonctionne donc comme une autre. Si un plugin d’optimisation reporte, combine ou réécrit les scripts, vérifiez que la balise arrive intacte : non vérifié avec les extensions courantes.
3. La fonction de vérification
Enregistrez la fonction PHP de la documentation dans antisy.php, à côté du fichier précédent, sans la modifier hors la ligne du secret :
<?php
/**
* Vérifie le jeton Antisy (mode simple). Rend le verdict : "allow" ou "deny".
*
* - jeton vide ou absent : envoyé tel quel, Antisy décide ; jeton non vide inutilisable : "deny", sans appel ;
* - Antisy ne répond pas (réseau, délai, 5xx) : "allow" et on journalise (fail-open) ;
* - 429 : ANTISY_ON_RATE_LIMIT ;
* - tout autre statut que 200 (400, 401, 413, 415...) : erreur de VOTRE appel, journalisée,
* tentative non vérifiée : "deny". Jamais "allow".
*/
const ANTISY_ON_RATE_LIMIT = 'allow'; // 'deny' pour un formulaire sensible : un attaquant peut provoquer un 429
function antisy_verdict(mixed $token): string
{
if ($token === null) {
$token = ''; // champ absent : traité comme vide
}
// Vide : envoyé tel quel, Antisy décide (collecte allumée : deny ; éteinte : le moteur décide).
// Non vide : 1 à 2 048 caractères ASCII imprimables ; /D : « $ » ne tolère pas un saut de ligne final.
if ($token !== '' && (!is_string($token) || !preg_match('/^[\x20-\x7e]{1,2048}$/D', $token))) {
return 'deny'; // un tableau de formulaire, un jeton trop long ou piégé : ce que répondrait Antisy
}
$ch = curl_init('https://api.antisy.com/v1/siteverify');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 3, // secondes ; au-delà, fail-open
CURLOPT_FOLLOWLOCATION => false, // aucune redirection suivie (c'est le défaut, écrit pour qu'on ne l'allume pas) : une 3xx est une erreur de VOTRE appel
CURLOPT_POSTFIELDS => http_build_query([
'secret' => getenv('ANTISY_SECRET'), // secret siteverify ; jamais en dur ni côté client
'response' => $token,
]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$error = curl_error($ch);
curl_close($ch);
// Seul cas où l'on laisse passer : Antisy ne répond pas.
if ($body === false || $status >= 500) {
error_log("Antisy injoignable, envoi laissé passer (fail-open): {$error} (HTTP {$status})");
return 'allow';
}
if ($status === 429) {
error_log('Antisy 429 (voie saturée): ' . ANTISY_ON_RATE_LIMIT);
return ANTISY_ON_RATE_LIMIT;
}
if ($status !== 200) {
error_log("Antisy HTTP {$status} : erreur de l'appel, à corriger ; tentative non vérifiée");
return 'deny';
}
$result = json_decode($body, true);
if (is_array($result) && is_bool($result['success'] ?? null)) {
if ($result['success']) {
return 'allow'; // la seule acceptation
}
return 'deny'; // success false : refus (le verdict vaut « deny »)
}
error_log('Réponse Antisy illisible, tentative non vérifiée');
return 'deny';
}4. Protéger les commentaires
Le crochet pre_comment_on_post s’exécute avant que WordPress traite un commentaire :
<?php
// Les commentaires : le formulaire de WordPress envoie ses champs à wp-comments-post.php, sur votre site.
// Ce crochet s'exécute avant que le commentaire soit traité.
add_action('pre_comment_on_post', function () {
if (antisy_verdict($_POST['antisy-token'] ?? '') !== 'allow') {
wp_die('Envoi refusé.', 'Envoi refusé', ['response' => 403, 'back_link' => true]);
}
});WordPress envoie le formulaire de commentaires à wp-comments-post.php, sur votre site : c’est un POST vers votre propre origine, que le script couvre. wp_die répond 403 et propose un lien de retour.
5. Protéger un formulaire de contact écrit à la main
Le formulaire envoie à admin-post.php, qui est sur votre site, avec un champ caché action qui choisit le crochet :
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
<input type="hidden" name="action" value="antisy_contact">
<label>Nom <input name="name" autocomplete="name"></label>
<label>E-mail <input name="email" type="email" autocomplete="email"></label>
<label>Message <textarea name="message"></textarea></label>
<button>Envoyer</button>
</form><?php
function antisy_contact_handler(): void
{
if (antisy_verdict($_POST['antisy-token'] ?? '') !== 'allow') {
wp_die('Envoi refusé.', 'Envoi refusé', ['response' => 403, 'back_link' => true]);
}
// … nettoyer les champs, puis envoyer ou enregistrer le message (wp_mail, article, base…)
wp_safe_redirect(home_url('/merci/'));
exit;
}
add_action('admin_post_nopriv_antisy_contact', 'antisy_contact_handler'); // visiteur non connecté
add_action('admin_post_antisy_contact', 'antisy_contact_handler'); // visiteur connectéCet exemple ne traite que la vérification anti-bot. Le nettoyage des champs, l’envoi du message et les protections propres à WordPress restent les vôtres.
Et les extensions de formulaires ?
Les extensions de formulaires (contact, devis, inscription) ont chacune leur façon de recevoir et de valider un envoi. Ce guide ne les couvre pas, ni la page de connexion de WordPress : non vérifié. Le script couvre un formulaire POST vers votre site même quand la page l’envoie par fetch, mais la vérification serveur doit être branchée à l’endroit où l’extension valide l’envoi, ce que ce guide ne décrit pas.
Que répond la vérification ?
La réponse est un objet JSON. success vaut true si et seulement si verdict vaut allow. Il n’y a que ces deux issues, et la réponse n’en donne jamais la raison : le refus que vous montrez au visiteur est générique lui aussi.
Un jeton est valable 60 secondes et ne se vérifie qu’une fois. Un second appel avec le même jeton est refusé.
{
"success": true,
"verdict": "allow",
"flags": [],
"error-codes": [],
"challenge_ts": "2026-09-24T10:00:00Z",
"hostname": "shop.example",
"action": "contact",
"event_id": "evt_01J7X4M2Q9K8ZP3R5T6V7W8Y9A"
}Voir : Documentation : la requête, la réponse et les codes d’erreur
Et si Antisy ne répond pas ?
La fonction laisse passer l’envoi dans un seul cas : Antisy ne répond pas (réseau coupé, délai de 3 secondes dépassé, erreur 5xx). Une panne d’Antisy ne doit pas fermer votre formulaire.
Toute autre erreur HTTP (400, 401, 413, 415) vient de votre appel : la fonction refuse l’envoi et le journalise, jamais un laissez-passer. Un 429 est une décision qui vous appartient : la constante ANTISY_ON_RATE_LIMIT vaut allow par défaut ; mettez deny pour un formulaire sensible.
Voir : Documentation : la requête, la réponse et les codes d’erreur
Que se passe-t-il si le script est bloqué ?
Une politique CSP qui refuse le script, un bloqueur de publicité, une page en HTTP ou un visiteur sans JavaScript empêchent le script de tourner. Le formulaire part alors quand même, sans le champ antisy-token : lisez-le avec une valeur vide par défaut.
Ce qui suit dépend du réglage « Collecte du navigateur » du site. Collecte allumée, un envoi sans jeton ne passe pas ; collecte éteinte, l’absence de jeton n’est pas reprochée. Un site neuf naît avec la collecte éteinte.
Ce qu’un contrôle anti-bot ne remplace pas
Aucun contrôle anti-bot n’est parfait, et Antisy n’annonce aucun taux de détection. Il s’ajoute aux autres défenses d’un formulaire, comme la limitation du nombre d’envois ; il ne les remplace pas.
Questions fréquentes
Existe-t-il un plugin Antisy pour WordPress ?
Non. Antisy ne propose pas d’extension WordPress à ce jour. L’intégration se fait à la main : une balise dans le <head> du thème ou d’un plugin à vous, et la fonction PHP de la documentation sur le crochet qui reçoit le formulaire.
Où mettre le secret d’Antisy dans WordPress ?
Dans une variable d’environnement du serveur si votre hébergeur en offre une, sinon dans wp-config.php, avec la clé de site. Le secret ne va jamais dans le dépôt d’un thème, dans la page ni dans un journal.
Un cache de pages ou un CDN gêne-t-il le jeton Antisy ?
Le jeton n’est pas dans le HTML : le script le crée à l’envoi, dans le navigateur de chaque visiteur. Une page en cache fonctionne donc comme une autre. Le risque est ailleurs : un plugin d’optimisation qui réécrit les scripts peut abîmer la balise, et ce guide ne l’a pas vérifié avec les extensions courantes.
Voir aussi : Protéger un formulaire PHP contre les robots, sans énigme · Protéger un formulaire Next.js contre les robots, sans énigme · Tous les guides · Documentation : guide pas à pas