NisuformDocs

Cloudflare Turnstile

Require a Cloudflare Turnstile challenge on your form so automated submissions are rejected before they are stored.

Cloudflare Turnstile is a free, privacy-friendly alternative to CAPTCHAs. Most visitors never see a puzzle. You use your own Turnstile widget, and Nisuform verifies its token on every submission. It works on every plan.

Set it up

Create a Turnstile widget

In the Cloudflare dashboard, open Turnstile and add a widget. Add every hostname your form is on, like example.com and www.example.com. Copy the site key and the secret key.

Add the keys to your form

Open the form's Settings, Spam protection. Turn on Require Cloudflare Turnstile, paste the Site key and the Secret key, and save.

The secret key is write only. Once saved, it is never shown again. Paste a new one to replace it.

Add the widget to your form

If you use the styled embed, copy it again from Get code in the builder or from the Setup tab. It includes the widget once Turnstile is on.

For your own markup, load the Turnstile script and place the widget inside the form:

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

<form action="https://api.nisuform.com/s/YOUR_FORM_KEY" method="POST">
  <input type="email" name="email" required>
  <textarea name="message" required></textarea>
  <div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITE_KEY"></div>
  <button type="submit">Send</button>
</form>

The widget adds a hidden cf-turnstile-response field to the form. Nisuform checks it, and never stores it.

Add the widget to your site before you turn Turnstile on, or right after. While Turnstile is on, every submission without a valid token is rejected.

With fetch

When you submit with JavaScript, send the token along with your fields. FormData picks it up from the form:

const response = await fetch(form.action, {
  method: 'POST',
  headers: { Accept: 'application/json' },
  body: new FormData(form),
})
const result = await response.json()
if (result.error === 'captcha_failed') {
  turnstile.reset()
}

A token can only be used once. Reset the widget after a failed submit so the visitor gets a fresh token.

When the check fails

A missing, expired, reused or invalid token is rejected with 403 and captcha_failed, and the message "Captcha verification failed. Complete the captcha and try again". Nothing is stored, so the visitor can solve the challenge and send again.

If every submission fails:

  • Check that the site key in your page and the keys in your form settings belong to the same widget.
  • Check that your site's hostname is in the widget's hostname list in Cloudflare.
  • Check that the widget is inside the <form> element, so its field is sent.