splitforms.com
← Back to the journal

Add Cloudflare Turnstile to Any HTML Form (2026)

Step-by-step Cloudflare Turnstile setup for a plain HTML form: widget markup, server-side cf-turnstile-response verification, and when to skip CAPTCHA.

Start free — 500 submissions/moSee pricing →No credit card. Paid plans from $1/mo.

What is Cloudflare Turnstile?

Cloudflare Turnstile is Cloudflare's free CAPTCHA replacement: a widget that checks whether a visitor is human without making them click traffic lights or type distorted letters. It runs a background set of browser and environment checks and returns a pass/fail token — the same basic contract as reCAPTCHA or hCaptcha, but built with privacy as the starting point rather than an add-on.

Turnstile ships in three widget modes. Managed is the default — Cloudflare decides per visitor whether to stay fully invisible or show a single, non-image checkbox. Non-Interactive never shows a challenge but still renders a small always-visible badge. Invisiblerenders nothing at all. None of the three ever show an image-selection puzzle — that's the main visible difference from reCAPTCHA v2 or hCaptcha's classic challenge.

Cloudflare markets Turnstile as free, with no paid Cloudflare plan required to use it. Exact terms and any usage limits can change, so treat "free" as the durable fact and confirm anything more specific directly on Cloudflare's site.

Get your sitekey and secret key

Log into the Cloudflare dashboard and open Turnstile in the sidebar, then Add site (sometimes labeled Add widget). Give it a name, enter the domain(s) the widget will run on — add localhost too if you want to test locally — and pick a widget mode; Managed is the sane default for a contact form.

Cloudflare then issues two keys. The Site Key is public — it goes straight into your HTML and is safe for anyone to see. The Secret Key is private: it belongs on your server only, in an environment variable, and should never appear in client-side HTML, JavaScript, or a public repo. Server-side verification is the only place it gets used.

Add the Turnstile widget to your HTML form

Two pieces go into your page: the Turnstile script, loaded once, and a div carrying your site key wherever you want the widget to render inside the form.

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

<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITEKEY"></div>

Here's the full pattern dropped into a plain HTML form that posts to splitforms — access key, a couple of fields, the Turnstile widget, and a honeypot field for good measure:

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

<form action="https://splitforms.com/api/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />

  <label>Name</label>
  <input type="text" name="name" required />

  <label>Email</label>
  <input type="email" name="email" required />

  <label>Message</label>
  <textarea name="message" required rows="4"></textarea>

  <!-- Turnstile widget -->
  <div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITEKEY"></div>

  <!-- Honeypot — leave this alone, bots fill it, humans never see it -->
  <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" />

  <button type="submit">Send message</button>
</form>

That renders a working widget. It does not, by itself, stop anything — the token it produces is only useful once something on the receiving end checks it, which is the entire next section.

Server-side verification: don't skip this part

The widget runs in the visitor's browser and only proves something to whatever checks its output. A bot that ignores your page's JavaScript entirely and POSTs straight to your form endpoint never touches the widget at all — so if nothing on your server looks at the result, Turnstile is decorative. When the widget does complete, it injects a hidden field named cf-turnstile-response into the surrounding form; that token is what you verify.

Turnstile server-side verification is one POST request: send the visitor's token and your secret key to Cloudflare's siteverify endpoint and check the success field it returns. A minimal Node example:

// Node 18+ has fetch built in — no extra dependency needed.
app.post("/contact", async (req, res) => {
  const token = req.body["cf-turnstile-response"];
  if (!token) return res.status(400).send("Missing Turnstile token.");

  const params = new URLSearchParams();
  params.set("secret", process.env.TURNSTILE_SECRET_KEY);
  params.set("response", token);
  params.set("remoteip", req.ip);

  const verify = await fetch(
    "https://challenges.cloudflare.com/turnstile/v0/siteverify",
    {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: params,
    }
  );
  const outcome = await verify.json();

  if (!outcome.success) {
    return res.status(400).send("Verification failed — please try again.");
  }

  // Token is valid. Continue handling the submission (save it, email it, etc).
});

That's the whole contract — a form-encoded POST and a boolean to check, no SDK required. The part worth planning for is everything around it: hosting this function, keeping the secret out of source control, and deciding what happens when Cloudflare's own endpoint times out, which the next section hands off.

The managed path: let splitforms verify it for you

If your form already posts to https://splitforms.com/api/submit, you don't need to write or host the function above. Paste your Turnstile secret key into Dashboard → Settings → Spam & security and splitforms reads the cf-turnstile-response field automatically on every submission and verifies it server-side against Cloudflare before the lead ever reaches your inbox. The widget markup from the previous section is all the client-side work required — see everything else that runs by default on the spam-protection feature page.

One behavior worth understanding before you rely on it: verification fails open. If no secret is configured, the check simply passes — expected, since you haven't turned it on. If Cloudflare's siteverify endpoint is unreachable or times out during a genuine outage, the check also passes rather than blocking submissions. It only fails — and blocks the submission — when a token is present and Cloudflare explicitly comes back and says that token is invalid.

That trade-off is deliberate. For a lead form, silently swallowing every submission during a Cloudflare outage is worse than one bad actor slipping through that same window — a missed sales inquiry costs real money; one stray spam email doesn't. Fail-open protects the leads; "invalid token still blocks" keeps the everyday spam-blocking real.

Turnstile vs reCAPTCHA vs nothing at all

Here's the honest version: most contact forms don't need Turnstile, reCAPTCHA, or any visible CAPTCHA on day one. A server-side stack — a honeypot field, a time-trap on submissions that arrive suspiciously fast, and IP-based rate limiting — stops the large majority of contact-form spam with zero friction: no widget, no third-party script, no consent-banner question to answer. If you want that setup directly, the spam-free contact form guide walks through it.

Add Turnstile once you've measured spam that gets past those layers — not before. Between the two mainstream CAPTCHA options, Turnstile is generally the better default: free, less visible friction, and no tie to a Google identity or cookie. The hCaptcha vs reCAPTCHA comparison and the wider best CAPTCHA for contact forms roundup score it directly against the alternatives; for avoiding Google's stack specifically, see reCAPTCHA alternatives. Not sure you have a spam problem worth solving yet? The free spam test tool scores a pasted message first.

Troubleshooting Turnstile

  • Widget not rendering. Usually a Content-Security-Policy that blocks challenges.cloudflare.com, or a browser ad/script blocker silently stripping the script tag. Check the browser console for a blocked-request warning, and make sure your CSP's script-src and frame-src allow that domain.
  • Token expired. A Turnstile token is valid for about 300 seconds after the widget renders. If a visitor sits on the page longer than that before submitting, or the page loaded from a back-forward cache, verification fails on an otherwise-real submission — call Turnstile's reset method to re-render the widget rather than trusting a stale token.
  • Testing on localhost. Cloudflare publishes always-pass test keys for local development: sitekey 1x00000000000000000000AA paired with secret 1x0000000000000000000000000000000AA. The widget always renders and always verifies successfully with this pair — swap in your real keys before deploying, since the test pair will also pass a bot's request.

FAQ

Is Cloudflare Turnstile free?

Yes — Cloudflare positions Turnstile as a free CAPTCHA replacement, with no paid Cloudflare plan required. Terms and usage limits can shift over time, so confirm specifics on Cloudflare's site beyond the basic fact that it's free.

What is cf-turnstile-response?

It's the hidden field the Turnstile widget adds automatically to the form it sits in, once it finishes checking the visitor. You don't create this field yourself — the widget's script injects it, and its value is the token your server (or splitforms) sends to Cloudflare's siteverify endpoint to confirm the visitor passed.

Does adding the widget alone stop spam?

No. The widget only runs in a visitor's browser. A bot that skips your page's JavaScript and POSTs straight to your form endpoint never triggers it at all. Turnstile only blocks anything once something server-side checks the cf-turnstile-response token and rejects submissions that fail.

How long is a Turnstile token valid?

About 300 seconds (5 minutes) from when the widget finishes rendering. A server-side check against an older token will fail — re-render the widget rather than reusing one that's gone stale.

Should I use Turnstile or reCAPTCHA?

Turnstile, for most new builds — it's free, shows a visible challenge less often, and doesn't tie the check to a Google identity or cookie. reCAPTCHA v3's larger cross-site signal graph can edge it out against sophisticated, targeted spam, but that's rarely the deciding factor for an ordinary contact form.

Do I still need a honeypot if I add Turnstile?

Yes, keep it. A honeypot field costs nothing to run and catches the lazy bots that fill in every field they find before Turnstile's check is even relevant. They solve different problems, so running both isn't redundant.

Can I test Turnstile on localhost?

Yes. Cloudflare publishes dedicated test keys that always render and always pass verification: sitekey 1x00000000000000000000AA with secret 1x0000000000000000000000000000000AA. Swap in your real keys before deploying — the test pair passes literally any request, including a bot's.

What happens to my form if Cloudflare's verification service goes down?

If splitforms is verifying it for you, nothing breaks: a network failure reaching Cloudflare's siteverify endpoint is treated as a pass, so an outage never blocks a real lead — it only rejects a submission when a token is present and explicitly invalid. If you're verifying it yourself, build that same fail-open logic in, or an outage will silently block every submission.

Want cf-turnstile-response verified automatically, without hosting the siteverify call yourself? Get a free splitforms access key — paste your Turnstile secret into Spam & security and verification runs server-side on every submission, fail-open included, free on every plan including Free (500 submissions/month, unlimited forms).

Related articles

More practical guidance from spam & security.

Browse the journal →
Spam & Security

hCaptcha vs reCAPTCHA: Privacy, Pricing, and Accuracy

hCaptcha vs reCAPTCHA in 2026 — privacy posture, GDPR fit, pricing, and accuracy compared, p

9 min readRead →
Spam & Security

Contact Form Security: How to Protect Form Submissions in 2026

Contact form security checklist for 2026: HTTPS, input validation, XSS prevention, spam prot

12 min readRead →
Spam & Security

Form Submission Rate Limiting: Stop Spam and Abuse

Implement rate limiting on form submissions to stop spam floods, resource exhaustion, and ab

10 min readRead →

Explore this topic

Start with the overview, then move into focused guides.

OverviewForm Spam Protection — Complete Guide (2026)Start here →GuideWhat Is a Honeypot Field?Read →GuideHoneypot vs reCAPTCHARead →GuideHow to Stop Contact Form SpamRead →ReferenceSpam protection guideOpen →

Building forms with ChatGPT, Claude, Cursor, or v0? Connect the native MCP server and give your agent a production form backend.

Explore the MCP server →

Give your form a production backend.

One endpoint adds delivery, spam filtering, storage, and integrations. Start with 500 submissions a month for free.

Create free accountRead the docs →
Secure checkoutSSL encryptionPrivacyProtected
VISAAMERICANEXPRESSstripe