how-to
Protect Your Form with Cloudflare Turnstile
Turn Turnstile on from your form settings, or bring your own Cloudflare keys
Turnstile needs a server. Your static site does not have one
Cloudflare Turnstile asks your visitor for a click at most, and usually for nothing at all, which is why people want it instead of hunting for traffic lights in a grid of photos. Every Turnstile walkthrough then hits the same wall: the widget on your page is only half of the check. Something has to take the token the widget produces and ask Cloudflare whether it is genuine, and that something has to run on a server. A static site is precisely the thing that does not have one.
Your form already posts to an Un-static endpoint, so the server half has somewhere to live. There are two ways to use it, and what separates them is who owns the Cloudflare keys.
- Our keys. Pick Cloudflare Turnstile on the form’s settings page and stop. No Cloudflare account, no keys, no change to your HTML.
- Your keys. Create a Turnstile widget in your own Cloudflare dashboard and store its secret on the form. You decide where the widget sits and how it looks.
Either way the token is checked from our servers against Cloudflare, never in the browser. The choice is per form, so one endpoint can run Turnstile while the next one stays on reCAPTCHA.
What you need first
- A form endpoint linked to your Un-static account. The spam-protection setting lives on a linked form’s settings page. The admin page of an anonymous “just a form” endpoint shows its captcha row read-only, marked as needing a linked account, so those endpoints stay on our protection page and cannot be switched to Turnstile. Linking an endpoint you already have is free.
- Any plan, Free included. Captcha is not a paid feature here. Nothing below costs extra on any plan. See the plan comparison for what the plans do differ on.
- A Cloudflare account, for the second option only. Turnstile is free at Cloudflare too, but you only need an account there if you want to run the widget on your own keys.
If there is no form on your site yet, start with adding a contact form to a static site and come back here.
Option 1. Let Un-static run Turnstile for you
The whole setup:
- Open the form in your Un-static dashboard and go to Security Settings.
- In the Spam-protection provider section, set Provider to Cloudflare Turnstile. The other options are Google reCAPTCHA, hCaptcha, and Off (no spam protection).
- Click Save provider. Leave your HTML alone.
There is no site key to copy and no script tag to paste, because the widget does not run on your page.
What your visitor sees
Your form posts to us the way it always did. Instead of going straight on to your redirect or thank-you page, the visitor gets one page from us on forms.un-static.com, headed Verify you are human, with the Turnstile widget underneath it and no submit button. Turnstile usually settles it by itself. The moment it passes, the page sends the held submission on and the visitor continues to wherever your form was already going to send them.
For most people this is a page that flashes past, not a puzzle to solve. It is the same protection page the reCAPTCHA setup uses, rendered with the Turnstile widget instead. It does need JavaScript in the visitor’s browser, which is true of any captcha widget.
This route needs a normal form post
The protection page is an HTML page we send back in answer to the submission, so it cannot appear in the middle of a fetch() or jQuery.ajax() call. If your form submits with JavaScript, use option 2 and put the widget on your own page. The AJAX contact form how-to covers the rest of that setup.
Option 2. Use your own Cloudflare Turnstile keys
Bring your own keys when you submit with AJAX, when you want the widget in a particular spot in your layout, when you want control over how the widget looks, or when you want the traffic under your own Cloudflare account.
Create the widget at Cloudflare
In the Cloudflare dashboard, add a Turnstile widget. It gives you two values: the site key, which is public and goes in your HTML, and the secret key, which is private and goes on your form. Add the hostname your form lives on to the widget’s allowed hostnames, or Cloudflare refuses to render it there.
Set the provider, then store the secret
Set the provider first, exactly as in option 1: Security Settings, Spam-protection provider, Provider to Cloudflare Turnstile, Save provider. The secret you store next belongs to whichever provider the form is set to, and a form still on reCAPTCHA looks for g-recaptcha-response, so it refuses every submission that carries a Turnstile token instead.
Then, on the same page, under Set captcha secret, paste the secret key into Your own captcha secret and save. From that point we stop serving the protection page for this form. We expect the widget to be on your page and its token to arrive with the submission.
Add the widget and the script to your form:
<form method="post" action="https://forms.un-static.com/forms/YOUR_ENDPOINT_REFERENCE">
<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>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
Cloudflare’s script writes a hidden input called cf-turnstile-response into your form, and that is the field we read when the submission lands. You do not add that input yourself. If you post the form with JavaScript, make sure that field’s value goes out with the rest of your data.
Every provider has its own field name, so moving a form from reCAPTCHA means swapping g-recaptcha-response for cf-turnstile-response. hCaptcha uses h-captcha-response.
Because the markup is yours in this mode, the widget’s appearance is yours too. Anything Cloudflare supports on the cf-turnstile element, such as a theme or a size, you can set here.
Optional: store the site key as well
Once a secret is stored, a second block appears, Set captcha site key (optional). Put the matching public site key in it and the behaviour flips back: we serve the protection page again, rendered with your keys instead of ours, and you can take the widget back out of your own HTML. For that to work, add forms.un-static.com to the widget’s allowed hostnames at Cloudflare. A site key on its own is refused, because there would be no secret to verify its tokens with: the API answers with a 422, and the dashboard does not show the field at all until a secret is stored.
Once you have stored a secret, the settings page also grows a Use our protection block with a Back to our protection page button. That clears your secret and your site key in one go and puts the form back on our keys. The provider stays on Cloudflare Turnstile.
So a form set to Turnstile has three states:
| What you stored | Where the widget runs | What goes in your HTML |
|---|---|---|
| Nothing | Our protection page, our keys | Nothing |
| Secret only | Your own page | The widget and the script |
| Secret and site key | Our protection page, your keys | Nothing |
All three are reachable from the API as well: PUT /api/forms/{ref} takes captcha_provider, recaptcha_secret and captcha_site_key, and the forms API reference has the request and response shapes. Storing a secret on a form whose captcha is off turns captcha back on and picks reCAPTCHA, unless you send captcha_provider in the same call.
One trap applies to both the API and the dashboard: changing the provider never clears a secret or a site key already stored on the form. Whatever is stored is what we send to the new provider, which fails there and gets you the wrong-secret mail below until you replace it. In the dashboard the provider and the secret are two separate saves, so do them one after the other, or press Back to our protection page first to clear both and start clean.
How the check actually runs
In all three states the verdict is settled on our side. When the submission reaches us we POST the secret and the token to https://challenges.cloudflare.com/turnstile/v0/siteverify and read Cloudflare’s answer. Editing the page or writing your own value into the hidden input gets you nowhere, because the token has to be one Cloudflare issued, for that key, and has not already been spent.
This is also why the widget alone is not protection. A bot that posts straight to your endpoint never loads your page and never sees the widget. What stops it is the check behind the endpoint.
A wrong secret costs you spam, not messages
If the secret stored on your form stops matching your Cloudflare widget, we do not quietly drop the submission. We let it through and e-mail you that your saved Cloudflare Turnstile secret is not valid anymore and that we cannot stop bots for you until you fix it. A rotated or mistyped key gets you spam for a while; it never loses you a real message. Put the right secret back and the check resumes on the next submission.
Test it before you trust it
Submit your own form once from your site and watch which of the two things happens. On our keys, the protection page appears with the Turnstile widget and then hands you on to your thank-you page. On your own keys with no site key stored, the widget on your own page settles and the submission goes through in one step.
Your form’s settings page also links a built-in test form that renders the captcha with the keys the form is really using, which is the fastest way to see the widget without touching your site. It works in every state but one: with a stored secret and no stored site key, the link sends you straight back to the settings page with a message, because then we have no site key to render a widget with.
If a submission does not arrive, work down this list:
- Nothing arrives at all. With a secret stored and no site key we require a token on every submission, so a missing or empty
cf-turnstile-responseis a refused submission. Check that the script tag is on the page and that the widget rendered. - The widget shows an error instead of a challenge. Cloudflare is refusing that site key for that hostname. Add the hostname to the widget’s allowed list. If the widget is on our protection page, the hostname to add is
forms.un-static.com. - Submissions arrive and you also get mail about the captcha. The stored secret is not valid for that widget. Copy it from Cloudflare again.
- You changed the provider and the old captcha still shows. The setting is per form. Check you changed it on the endpoint your page actually posts to.
What this does not do
Worth saying plainly, because a lot of Turnstile pages are vague about it:
- It is per form, not per account. Switching one form leaves the rest alone. Useful, but it also means you set it once per endpoint.
- We do not choose the widget mode. We store a provider and a secret, nothing more. Managed, non-interactive and invisible are settings on the widget in your Cloudflare dashboard. On our keys you get whatever our widget is configured as; on your keys with the widget on our protection page you get whatever you configured; with the widget on your own page you write the markup, so all of it is yours.
- It is not new. Per-form captcha providers shipped on 29 June 2026, and the changelog entry for choosing your spam-protection provider has the detail. This page is documentation catching up with the product, not an announcement.
- It does not replace the rest of the spam handling. Our content filtering keeps running underneath, as does the honeypot field if you set one on the same settings page, and both catch things a captcha never sees. Turnstile is one layer of several.
Questions people ask
Do I need a Cloudflare account to use Turnstile on my form?
Not for option 1. Picking Cloudflare Turnstile on the form’s settings page runs the widget on Un-static’s own Turnstile keys, so there is nothing to sign up for and nothing to copy across. You need a Cloudflare account only if you want the widget on your own page, or the traffic under your own account.
Does adding the Turnstile widget to my page stop spam on its own?
No, and this is the part most Turnstile guides skip. The widget produces a token and nothing else. If no server checks that token with Cloudflare, a bot can post to your endpoint without ever loading your page, and the widget never gets a look at it. On Un-static the check runs on our side on every submission, which is what makes having the widget worth anything.
Can I use Turnstile with an AJAX form?
Yes, with your own keys. Our protection page is an ordinary HTML response, so it cannot appear in the middle of an XMLHttpRequest or fetch(). For a JavaScript submission, store your Turnstile secret on the form, put the widget on your page, and send the cf-turnstile-response value along with your other fields.
Is Turnstile free on Un-static?
Yes. Captcha is not gated by plan here, so it behaves the same on the Free plan as on a paid one, and Cloudflare does not charge for Turnstile either. The one requirement is a form endpoint linked to an account, which is also free.
Can I switch back to reCAPTCHA or hCaptcha later?
Yes, it is the same dropdown. The four options are Google reCAPTCHA, Cloudflare Turnstile, hCaptcha and Off, the setting is per form, and changing one form leaves every other form on your account untouched. Hosting your own reCAPTCHA covers the Google side if you go back.
Let us know if you run into any issues!