4 November 2026 · 9 min read · BlitzHash Team · Cloud, DevOps & Security
Cloudflare Turnstile Signup Protection: 5 Tests That Prove It Blocks Bots
Short answer: Cloudflare Turnstile on your signup form only stops bots if your server checks every token with Cloudflare's Siteverify API. The widget on its own is decoration. A bot can skip the page, post straight to your signup endpoint with a made-up token, and create accounts that burn your email quota, your auth limits and your AI credits. Add Turnstile to the form, verify the token on the server (or turn on your auth provider's built-in CAPTCHA check), and then run the five tests below to prove it actually blocks a forged request. If you'd rather have someone confirm it for you, our free technical assessment covers bot signups along with the rest of your launch risks.
This guide is written for founders who built their app with Lovable, Bolt, Cursor, Replit or v0 and now see strange signups: random Gmail addresses, names like "asdfgh", bursts at 3 a.m., or an AI bill that jumped without new paying users.
Why bot signups cost more in an AI-built app
In a classic website, a fake signup is mostly noise. In an AI-built app it usually triggers paid work the moment the account exists:
- A welcome or confirmation email goes out through Resend, SendGrid or your auth provider, and those have sending limits. Hit them and real users stop getting their magic links.
- A free trial of your AI feature starts, so each fake account can call OpenAI, Anthropic or another model on your key until its trial runs out.
- Your database fills with junk rows, which skews your analytics and makes "active users" meaningless.
- Your sender reputation drops when confirmation emails bounce off fake addresses, which pushes your real emails toward spam.
We covered the related problem of keys leaking from the browser in our guide to leaked OpenAI API keys. Bot signups are the quieter cousin: nobody steals the key, they just use your app the way you built it, thousands of times.
What Cloudflare Turnstile actually does on a signup form
Turnstile is Cloudflare's CAPTCHA alternative. Instead of making people click traffic lights, it runs checks in the browser and usually passes humans without any interaction. You choose one of three widget modes when you create it in the Cloudflare dashboard: Managed (Cloudflare decides whether to show a checkbox), Non-interactive (a visible widget that never asks for a click), and Invisible (nothing shown).
When the check passes, the widget hands your page a token. That token is the only thing that matters. According to Cloudflare's own documentation, each token:
- is valid for 300 seconds (5 minutes),
- can be validated only once, and
- means nothing until your server sends it to
https://challenges.cloudflare.com/turnstile/v0/siteverifywith your secret key and gets back"success": true.
Cloudflare's docs say it plainly: the client-side widget alone does not protect your forms, because an attacker can submit any string as the token.
The mistake AI builders make with Turnstile signup protection
When you ask an AI builder to "add Cloudflare Turnstile to signup", the usual result is a widget that appears on the page and a submit button that stays disabled until the widget passes. It looks finished. But the signup request that reaches your backend often never mentions the token at all, or it sends the token and nothing checks it.
There are three common versions of this:
1. Front-end only. The widget is there, the button waits for it, and the API route creates the user without reading the token. A bot calling the API directly never sees the widget. 2. Verified in the browser. The code calls Siteverify from the client, which means the secret key ships in your JavaScript bundle. Cloudflare's docs warn that this lets attackers bypass the check entirely. 3. Test keys left in production. Cloudflare publishes dummy keys for development. We tested them directly against Siteverify (results below), and the "always passes" test secret approved a token we simply typed in. If that secret is what your production server uses, Turnstile is approving every bot.
How to set up Cloudflare Turnstile signup protection the right way
Step 1: Create the widget and lock its hostnames
In the Cloudflare dashboard, open Turnstile, add a widget, and list only your real domains (for example yourapp.com and www.yourapp.com). Cloudflare recommends not allowing localhost on production site keys; use the dummy keys for local work instead. You'll get a site key (public, goes in the page) and a secret key (private, server only).
Step 2: Pick your server path
You have two good options:
Option A: your auth provider checks it for you. Supabase Auth supports Turnstile natively. Turn on CAPTCHA protection under the project's Authentication settings (Bot and Abuse Protection), choose Turnstile, paste the secret key, and pass the token in the signup call:
await supabase.auth.signUp({
email,
password,
options: { captchaToken },
})Once it's enabled, the auth server rejects sign-up, sign-in and password reset requests without a valid token, so a bot calling the auth endpoint directly gets blocked too. Remember to reset the widget after each attempt, because a used token can't be reused.
Option B: your own API route checks it. If signup goes through your own endpoint (a Next.js route handler, an Express route, a Cloudflare Worker), verify before you create anything:
// app/api/signup/route.js (Next.js example)
export async function POST(req) {
const { email, password, token } = await req.json();
const res = await fetch(
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
secret: process.env.TURNSTILE_SECRET_KEY,
response: token,
remoteip: req.headers.get("CF-Connecting-IP") ?? undefined,
}),
}
);
const result = await res.json();
if (!result.success || result.hostname !== "yourapp.com") {
return Response.json({ error: "Verification failed" }, { status: 400 });
}
// Only now: create the user, send the email, start the trial.
}The two details that matter: the secret comes from a server environment variable (never a NEXT_PUBLIC_ or VITE_ variable), and nothing expensive happens before success is true. Checking hostname confirms the challenge was solved on your own domain.
Step 3: Use real keys only in production
Set the dummy keys in development and test environments and the real keys only in production. A simple rule: if your production environment variable starts with 1x0000, something is wrong.
Five tests that prove Turnstile is really blocking bots
These are the tests we run on client apps. You need a terminal and about ten minutes. Replace the URL and body with your signup endpoint (your browser's Network tab shows it when you sign up normally).
Test 1: The forged-token request
Send a signup request with a made-up token:
curl -i -X POST https://yourapp.com/api/signup \
-H "Content-Type: application/json" \
-d '{"email":"bot-test-1@example.com","password":"Test-12345!","token":"made-up-token"}'Pass: a 400-type error and no new user in your database. Fail: a 200 and a new row. If it fails, your server isn't verifying tokens.
Test 2: The no-token request
Repeat the request with the token field removed entirely. Some code only verifies when a token is present. Pass: rejected. Siteverify itself answers a missing token with missing-input-response, so your code should treat that as a failure.
Test 3: The replay
Sign up once normally in the browser, copy the token from the Network tab, and send it again with a different email. Pass: rejected, because Cloudflare returns timeout-or-duplicate for a token that was already used.
Test 4: The test-key check
Ask whoever deployed the app (or check your hosting dashboard's environment variables) what TURNSTILE_SECRET_KEY is in production. It must not be one of Cloudflare's published dummy secrets.
We ran Cloudflare's dummy keys against the live Siteverify API on November 4, 2026. The "always passes" secret (1x0000000000000000000000000000000AA) returned "success": true both for the official dummy token and for a string we typed ourselves, with "result_with_testing_key": true in the response. The "always fails" secret returned invalid-input-response, the third dummy secret returned timeout-or-duplicate, and a made-up secret returned invalid-input-secret. So a forgotten test secret quietly turns your bot protection off, and the only clue is that metadata field. If you log Siteverify responses, alert on result_with_testing_key in production.
Test 5: The secret-in-the-bundle search
Open your live site, view the loaded JavaScript in DevTools (Sources tab), and search for siteverify and for your secret key's first characters. Pass: neither appears in browser code. The token should leave the browser; the secret should never arrive in it.
Turnstile signup checklist (copy this)
- [ ] Widget created with only production hostnames allowed
- [ ] Site key in the page, secret key only in server environment variables
- [ ] Token verified on the server (auth provider CAPTCHA setting or your own route) before any user, email or trial is created
- [ ] Missing, forged and replayed tokens all rejected (Tests 1 to 3)
- [ ] Production secret is not a
1x0000…,2x0000…or3x0000…dummy key - [ ] No
siteverifycall or secret in browser JavaScript - [ ]
hostname(andaction, if you set one) checked in the response - [ ] Widget reset after each attempt so users can retry
- [ ] Same protection on sign-in and password reset, not just signup
- [ ] Rate limit on the signup endpoint as a second layer
- [ ] Failed verifications logged so you can see bot attempts
Turnstile is one layer, not the whole wall
Turnstile raises the cost of automated signups, but it doesn't replace other basics:
- Rate limiting. A Cloudflare rate limiting rule on
/api/signupor your auth path caps how fast any single source can try. Turnstile stops cheap bots; rate limits stop persistent ones. - Email confirmation before the expensive part. Start the AI trial after the email is confirmed, not at signup. That one change protects your model budget even if a bot gets through.
- Spend limits on your AI provider. Set a monthly budget or usage limit in your OpenAI or Anthropic account so a bad week can't become a bad month.
- Watching the numbers. A daily count of signups against confirmed emails will show a bot wave within a day.
If you want ready-made Cloudflare configuration patterns, our open-source BlitzHash Cloudflare production skills repo collects the production checks we use for Cloudflare-fronted apps. For the wider picture of what else attackers try on AI-built apps, see our application security audit checklist in plain English, and if you're on Replit, our launch guide covers auth, rate limits and backups.
FAQ
Does Cloudflare Turnstile hurt signup conversion?
It's designed to stay out of the way: in Managed mode most visitors never have to click anything, and Invisible mode shows no widget at all. The bigger conversion risk is a broken setup, for example an expired token (after 5 minutes) that makes the form fail with no message. Reset the widget on errors and show a clear "please try again" message.
Do I need my site on Cloudflare to use Turnstile?
No. Turnstile works on any site, whether it's hosted on Vercel, Netlify, Replit or elsewhere. You only need a free Cloudflare account to create the widget and get keys.
Is the Supabase CAPTCHA setting enough, or do I need my own check too?
If users sign up through Supabase Auth directly, enabling Turnstile in Supabase's auth settings is enough for sign-up, sign-in and password reset, because Supabase's server verifies the token. You still need your own check on any other public form you build, such as a waitlist, contact form or an endpoint that calls your AI model.
Why does my Turnstile test pass locally but fail in production?
Usually because the production secret is paired with a dummy site key (or the other way round). Cloudflare's docs note that production secrets reject the dummy token and dummy secrets reject real tokens. Check that the site key and secret key come from the same widget, and that your production hostname is on the widget's allowed list.
Get a second pair of eyes
Bot protection is one of those things that looks done long before it is. If you'd like us to run these tests against your app, along with checks for leaked keys, open databases and launch readiness, book a free technical assessment. You can also start with our free self-check to see where your app stands today.
Sources
- Cloudflare Turnstile docs: Validate the token (Siteverify)
- Cloudflare Turnstile docs: Test your Turnstile implementation
- Supabase docs: Enable CAPTCHA protection
