Protéger vos formulaires avec Cloudflare Turnstile
Cloudflare Turnstile est une alternative moderne et respectueuse de la vie privée aux CAPTCHAs. Il détecte les bots sans friction, sans soumettre les utilisateurs à des défis, améliorant ainsi à la fois l’accessibilité et la sécurité des formulaires.
Formspree prend en charge Turnstile nativement — il suffit de configurer vos clés et d’activer le CAPTCHA dans les paramètres de votre formulaire.
Étape 1 : Créer un site Turnstile
Commencez par vous rendre sur le tableau de bord Cloudflare Turnstile.
Cliquez sur Add widget pour créer un nouveau site Turnstile, puis :

- Saisissez un nom pour votre site.
- Ajoutez votre domaine (par ex.
example.com) sous Allowed Domains. Utilisez localhost pour le développement local. - Choisissez le mode du widget.

Enregistrez la configuration. Une fois créé, vous verrez vos nouvelles clés de widget.

Copiez la clé de site et la clé secrète — vous en aurez besoin dans votre formulaire HTML et dans la configuration Formspree.
Étape 2 : Configurer le CAPTCHA dans Formspree
Accédez à la page des paramètres de votre formulaire dans le tableau de bord Formspree.
Dans la section CAPTCHA, assurez-vous que la protection CAPTCHA est activée.
Cliquez sur Adjust settings, sélectionnez Cloudflare Turnstile et collez votre clé secrète dans le champ prévu.

Enregistrez vos modifications.
Étape 3 : Ajouter Turnstile à votre formulaire HTML
Il est maintenant temps d’ajouter Turnstile à votre formulaire. Incluez le script Turnstile et votre clé de site dans votre code HTML.
Voici un exemple de base :
<html>
<head>
<title>Turnstile Demo</title>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</head>
<body>
<form action="https://formspree.io/f/{your-form-id}" method="POST">
<input type="email" name="email" placeholder="Email" required />
<div class="cf-turnstile" data-sitekey="{your-site-key}"></div>
<br />
<input type="submit" value="Submit" />
</form>
</body>
</html>
Lorsqu’un utilisateur soumet le formulaire, le widget Turnstile génère automatiquement un jeton (cf-turnstile-response) que Formspree vérifie à l’aide de votre clé secrète.
Utiliser AJAX (optionnel)
Si vous soumettez votre formulaire via AJAX, vous pouvez inclure manuellement le jeton Turnstile dans votre requête.
Voici un exemple avec du JavaScript Vanilla :
<!DOCTYPE html>
<html>
<head>
<title>Turnstile AJAX Demo</title>
<!-- Turnstile script -->
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</head>
<body>
<form id="form" method="POST" action="https://formspree.io/f/<form-hashid>">
<input type="email" name="email" placeholder="email@example.com" required />
<!-- Turnstile widget (automatically adds hidden cf-turnstile-response input) -->
<div class="cf-turnstile" data-sitekey="{site-key}"></div>
<br />
<button type="submit">Submit</button>
</form>
<script>
const form = document.getElementById("form");
form.addEventListener("submit", function (event) {
event.preventDefault();
// Grab all form fields, including the hidden cf-turnstile-response
const data = new FormData(form);
fetch(form.action, {
method: form.method,
body: data,
headers: {
"Accept": "application/json"
}
})
.then(function (response) {
return response.json();
})
.then(function (data) {
console.log("Formspree response:", data);
})
.catch(function (error) {
console.error("Error:", error);
});
});
</script>
</body>
</html>
Ou en utilisant l’API Turnstile :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"
defer
></script>
</head>
<body>
<form id="contact-form">
<input type="email" name="email" placeholder="Email" required />
<div id="turnstile-container"></div>
<button type="submit">Send</button>
</form>
<script>
let widgetId = null;
// Render widget when the page finishes loading
window.onload = function () {
if (!window.turnstile) {
console.error("Turnstile did not load.");
return;
}
widgetId = window.turnstile.render("#turnstile-container", {
sitekey: "{your-site-key}",
callback: function (token) {
console.log("Turnstile success:", token);
}
});
};
// AJAX submit
document.getElementById("contact-form").addEventListener("submit", function (event) {
event.preventDefault();
const form = this;
const formData = new FormData(form);
// Ensure we send the token under the standard field name
// If Turnstile already added a hidden input, this overwrites it.
formData.set("cf-turnstile-response", token);
fetch("/submit", {
method: "POST",
body: formData
})
.then((res) => {
if (!res.ok) {
throw new Error("Server error: " + res.status);
}
return res.text();
})
.then((text) => {
alert("Server replied: " + text);
// Reset for next submission
window.turnstile.reset(widgetId);
form.reset();
})
.catch((err) => {
console.error(err);
alert("Request failed: " + err.message);
});
});
</script>
</body>
</html>