Adding Cloudflare Turnstile to Next.js App (Free Plan)
Cloudflare Turnstile is a CAPTCHA alternative that's privacy-first, user-friendly, and completely free. Unlike traditional CAPTCHAs, Turnstile doesn't ask users to click on buses or traffic lights—it runs silently in the background and protects your forms with a simple token verification.
In this post, we'll add Turnstile to a basic contact form in a Next.js 14 App Router project using the free Cloudflare plan.
✅ Step 1: Sign Up and Get Site Key
- Go to dash.cloudflare.com/turnstile
- Add a new site
- Site name: e.g.
myapp.com - Widget type: "Managed" (recommended)
- Domain: Add your domain or use
localhostfor testing
- Site name: e.g.
- Copy the Site Key and Secret Key
🛠️ Step 2: Install Turnstile in Your Form
npm install react-turnstile
Create your form component, e.g. app/contact/page.tsx:
'use client';
import React, { useState } from 'react';
import Turnstile from 'react-turnstile';
export default function ContactForm() {
const [token, setToken] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const res = await fetch('/api/verify-turnstile', {
method: 'POST',
body: JSON.stringify({ token }),
});
const data = await res.json();
if (data.success) {
alert('Verified!');
} else {
alert('Verification failed');
}
};
return (
<form onSubmit={handleSubmit}>
<input name="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required />
<Turnstile
sitekey="YOUR_SITE_KEY"
onVerify={(token) => setToken(token)}
/>
<button type="submit">Send</button>
</form>
);
}
🔒 Step 3: Backend Verification (API Route)
Create app/api/verify-turnstile/route.ts:
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
const { token } = await request.json();
const secret = process.env.TURNSTILE_SECRET_KEY;
const res = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `secret=${secret}&response=${token}`,
});
const data = await res.json();
return NextResponse.json(data);
}
Don't forget to set TURNSTILE_SECRET_KEY in your .env.local:
TURNSTILE_SECRET_KEY=your_secret_key_here
🧪 Test It!
- Run your app:
npm run dev - Fill the form and submit
- You'll get a verified or failed message depending on the response
🧠 Final Tips
- Turnstile works with any plan, including free
- You can integrate it into login, signup, comments, or any form
- No need for user interaction = better UX than reCAPTCHA
Why Choose Turnstile?
- Privacy-focused: No tracking or personal data collection
- User-friendly: Often invisible to legitimate users
- Free: No cost for basic usage
- Easy integration: Simple API with good documentation
- Reliable: Powered by Cloudflare's global network
Turnstile is an excellent choice for protecting your forms while maintaining a great user experience. The free plan is perfect for most small to medium applications, and the setup process is straightforward with Next.js.