Skip to main content

Turnstile

Learning Focus

By the end of this lesson you will understand how Turnstile works, how to integrate it into your forms, and how to validate tokens on your server.

What Is Turnstile?

Turnstile is Cloudflare's free CAPTCHA alternative. Instead of making users solve puzzles (like reCAPTCHA), Turnstile uses invisible browser challenges to verify that a visitor is human — without friction.

flowchart LR
USER["User submits form"] --> TURNSTILE["Turnstile Widget\n(browser challenge)"]
TURNSTILE -->|"Token generated"| SERVER["Your Server"]
SERVER -->|"Verify token"| CF_API["Cloudflare\nVerification API"]
CF_API -->|"✅ Valid / ❌ Invalid"| SERVER

style TURNSTILE fill:#f6821f,color:#fff,stroke:#e5711e
style CF_API fill:#2563eb,color:#fff,stroke:#1e40af

Why Turnstile over reCAPTCHA?

FeatureTurnstilereCAPTCHA v2reCAPTCHA v3
User frictionNone (invisible) or minimal (one click)High (image puzzles)None (invisible)
PrivacyNo tracking, no cookies, GDPR-friendlyTracks users, sets cookiesTracks users, sets cookies
CostFree (unlimited)Free (with limits)Free (with limits)
Speed~100ms challenge10–30 seconds for user~100ms (but less accurate)
Hosted anywhere✅ Works on any site (no Cloudflare required)
AccuracyHighHigh (but annoying)Medium (score-based)
Key Advantage

Turnstile works on any website — you don't need to use Cloudflare as your CDN. It's a standalone widget you can add to any form.

Widget Modes

Turnstile offers three widget modes:

ModeUser ExperienceWhen to Use
ManagedCloudflare decides — invisible if confident, shows a checkbox if neededDefault — best for most use cases
Non-InteractiveAlways invisible — user never sees a widgetLogin forms, checkout — zero friction required
InvisibleNo visible widget at all — runs entirely in the backgroundAPI protection, background form validation

Setting Up Turnstile

Step 1: Create a Turnstile Widget

  1. Go to dash.cloudflare.com/turnstile
  2. Click "Add site"
  3. Enter your website's domain
  4. Select the widget mode (Managed, Non-Interactive, or Invisible)
  5. Copy your Site Key and Secret Key

Step 2: Add the Widget to Your HTML

login.html
<!DOCTYPE html>
<html>
<head>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</head>
<body>
<form action="/login" method="POST">
<input type="email" name="email" placeholder="Email" required>
<input type="password" name="password" placeholder="Password" required>

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

<button type="submit">Log In</button>
</form>
</body>
</html>

The widget renders automatically and generates a token (cf-turnstile-response) in a hidden form field when the challenge passes.

Step 3: Verify the Token on Your Server

When the form is submitted, your server receives the cf-turnstile-response token. You must verify it with Cloudflare's API:

server.py (Python/Flask example)
import requests
from flask import Flask, request

app = Flask(__name__)
SECRET_KEY = "YOUR_SECRET_KEY"

@app.route("/login", methods=["POST"])
def login():
token = request.form.get("cf-turnstile-response")

# Verify with Cloudflare
response = requests.post(
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
data={
"secret": SECRET_KEY,
"response": token,
"remoteip": request.remote_addr # optional
}
)

result = response.json()

if result.get("success"):
# Human verified — proceed with login
return "Login successful!"
else:
# Verification failed — block the request
return "Verification failed", 403
server.js (Node.js/Express example)
const express = require('express');
const app = express();
app.use(express.urlencoded({ extended: true }));

const SECRET_KEY = "YOUR_SECRET_KEY";

app.post('/login', async (req, res) => {
const token = req.body['cf-turnstile-response'];

const response = await fetch(
'https://challenges.cloudflare.com/turnstile/v0/siteverify',
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `secret=${SECRET_KEY}&response=${token}`
}
);

const result = await response.json();

if (result.success) {
res.send('Login successful!');
} else {
res.status(403).send('Verification failed');
}
});

Verification API Response

Successful verification
{
"success": true,
"challenge_ts": "2024-01-01T00:00:00Z",
"hostname": "example.com",
"action": "login",
"cdata": ""
}
Failed verification
{
"success": false,
"error-codes": ["invalid-input-response"]
}

Advanced Configuration

Custom Appearance

<div class="cf-turnstile"
data-sitekey="YOUR_SITE_KEY"
data-theme="dark"
data-size="compact"
data-language="auto">
</div>
AttributeOptionsDefault
data-themelight, dark, autoauto
data-sizenormal, compactnormal
data-languageauto, or ISO language codeauto
data-actionCustom action name (e.g., login, signup)none
data-callbackJavaScript function called on successnone

JavaScript API

Programmatic rendering
// Render manually
const widgetId = turnstile.render('#container', {
sitekey: 'YOUR_SITE_KEY',
callback: function(token) {
console.log('Challenge passed:', token);
},
theme: 'dark'
});

// Reset the widget
turnstile.reset(widgetId);

// Remove the widget
turnstile.remove(widgetId);

Testing Keys

Cloudflare provides test keys for development:

KeyBehavior
Site key: 1x00000000000000000000AAAlways passes
Site key: 2x00000000000000000000ABAlways blocks
Site key: 3x00000000000000000000FFForces interactive challenge
Secret key: 1x0000000000000000000000000000000AAAlways passes verification
Secret key: 2x0000000000000000000000000000000AAAlways fails verification

Common Misconceptions

"Turnstile only works on Cloudflare-proxied sites"

Reality: Turnstile works on any website, regardless of whether it uses Cloudflare as a CDN. It's a standalone product.

"Invisible mode means no security"

Reality: Invisible mode runs the full browser challenge in the background. It's just as secure — the user simply doesn't see a widget.

"I need to pay after hitting a certain number of verifications"

Reality: Turnstile is free and unlimited — no per-verification charges, no volume limits.

Key Takeaways

  • Turnstile is a free, unlimited CAPTCHA alternative — no puzzles, privacy-preserving.
  • It works on any site — not just Cloudflare-proxied domains.
  • Three modes: Managed (recommended), Non-Interactive, and Invisible.
  • Always verify the token server-side — never trust client-side validation alone.
  • Use test keys during development to simulate pass/fail scenarios.

What's Next

  • Continue to Challenges to learn about Cloudflare's challenge mechanisms.