{"success": true, "action": "contact", "cdata": ""}. Cette réponse de l’API siteverify de Cloudflare Turnstile semble suffisante pour valider une soumission de formulaire. Elle l’est, à condition que le code serveur qui la reçoit vérifie réellement chacun de ces champs, et non uniquement la valeur du booléen success. C’est précisément là que beaucoup d’intégrations laissent une brèche, alors même que le widget Turnstile s’affiche et fonctionne parfaitement côté visiteur.
Turnstile remplace le captcha visuel par une vérification silencieuse du comportement du navigateur, mais cette vérification ne vaut que par le contrôle qu’en fait le serveur qui reçoit le jeton généré. Voici le code de vérification côté serveur à écrire, et les erreurs fréquentes qui permettent à un bot de passer malgré une intégration Turnstile en apparence fonctionnelle.
Le snippet de vérification côté serveur
function verifier_turnstile( $jeton, $action_attendue ) {
$reponse = wp_remote_post( 'https://challenges.cloudflare.com/turnstile/v0/siteverify', array(
'body' => array(
'secret' => TURNSTILE_SECRET_KEY,
'response' => $jeton,
'remoteip' => $_SERVER['REMOTE_ADDR'],
),
) );
if ( is_wp_error( $reponse ) ) {
return false;
}
$corps = json_decode( wp_remote_retrieve_body( $reponse ), true );
if ( empty( $corps['success'] ) ) {
return false;
}
if ( isset( $corps['action'] ) && $corps['action'] !== $action_attendue ) {
return false;
}
return true;
}
Ce snippet couvre le cas de base : l’appel réseau vers siteverify, la vérification du succès, et un point souvent oublié, la vérification du champ action. Ce dernier point mérite une attention particulière : Turnstile permet de déclarer une action côté client au moment de générer le widget, et cette action se retrouve dans la réponse de vérification. Sans ce contrôle, un jeton valide obtenu sur un formulaire anodin du site pourrait être réutilisé pour valider un formulaire plus sensible.
Les erreurs qui laissent passer un bot malgré tout

Un widget Turnstile qui s’affiche et qui semble fonctionner ne garantit rien si l’intégration serveur comporte l’une des erreurs suivantes, observées à répétition sur des intégrations en apparence correctes :
- Vérifier le jeton une fois, l’accepter indéfiniment. Un jeton Turnstile est prévu pour un usage unique ; un code qui ne suit pas les jetons déjà consommés permet à un jeton intercepté d’être rejoué sur plusieurs soumissions.
- Ignorer le champ
action. Sans cette vérification, un jeton légitime obtenu sur le formulaire de newsletter peut valider une soumission sur le formulaire de contact, si les deux partagent la même clé de site. - Ne vérifier le jeton que côté JavaScript. Certaines intégrations se contentent d’un rappel
callbackcôté client qui débloque un bouton d’envoi, sans jamais renvoyer le jeton au serveur pour vérification réelle. Un bot qui court-circuite le JavaScript et soumet directement le formulaire HTML passe alors sans aucune vérification. - Timeout réseau silencieux traité comme un succès. Si l’appel à
siteverifyéchoue pour une raison réseau et que le code traite cet échec comme une validation par défaut plutôt que comme un rejet, la protection disparaît précisément quand elle est la plus nécessaire.
Empêcher le rejeu d’un jeton déjà validé
function turnstile_deja_utilise( $jeton ) {
$cle = 'turnstile_' . md5( $jeton );
if ( get_transient( $cle ) ) {
return true;
}
set_transient( $cle, true, 5 * MINUTE_IN_SECONDS );
return false;
}
Ce garde-fou complémentaire, à appeler juste après un success confirmé, empêche qu’un jeton intercepté par un tiers ou rejoué par erreur ne soit accepté deux fois. Cinq minutes de rétention suffisent largement, la durée de vie d’un jeton Turnstile étant elle-même courte.
Où placer la vérification dans le traitement du formulaire
La vérification Turnstile doit intervenir avant tout traitement métier du formulaire, jamais après :
- Réception de la soumission POST, extraction du jeton Turnstile transmis.
- Appel à
verifier_turnstile()avec l’action attendue pour ce formulaire précis. - Rejet immédiat avec message générique si la vérification échoue, sans traiter les autres champs du formulaire.
- Seulement ensuite, validation et traitement des champs métier (nom, email, message…).
Traiter les champs métier avant la vérification Turnstile, par exemple pour préremplir une confirmation d’erreur, expose inutilement la logique de validation à un bot qui n’a jamais réellement passé le défi.
Un anti-bot qui protège un formulaire de contact et un anti-bot qui protège un formulaire de commande n’ont pas la même exigence : le champ
actionexiste justement pour éviter qu’un jeton facile à obtenir sur l’un serve à valider l’autre.
Notre verdict
Turnstile réduit efficacement le bruit des soumissions automatisées quand l’intégration côté serveur suit ces quelques règles, mais le widget seul ne constitue jamais une protection : c’est l’appel à siteverify, la vérification de l’action et le suivi anti-rejeu qui, ensemble, ferment les portes qu’un bot bien conçu chercherait à emprunter.