Blog
4 min read

How to Add a CAPTCHA to Your Forms (Cloudflare Turnstile, reCAPTCHA, hCaptcha)

Stop bots spamming your signup, login and contact forms. How modern CAPTCHAs work, Turnstile vs reCAPTCHA vs hCaptcha, a working Cloudflare Turnstile example with the server-side check most tutorials forget, and when a CAPTCHA isn't the right tool.

Put a form on the internet and bots will find it within days: fake signups, spam messages, password-guessing on your login page, and — if the form triggers anything paid, like an AI call or an SMS — real money lost. A CAPTCHA is a check that tries to tell humans from bots before the form is accepted.

Modern CAPTCHAs are mostly invisible

The old CAPTCHAs — squiggly letters, "click every traffic light" — are slowly disappearing. Modern ones mostly run quietly in the background, looking at signals from the browser, and only show a challenge when something seems off. Most real visitors never notice them.

The main options

Cloudflare Turnstile Google reCAPTCHA hCaptcha
Cost Free Free tier, paid above it Free tier, paid features
Usually visible? Rarely v3 invisible; v2 shows a checkbox Sometimes
Privacy No ads tracking Google service Privacy-focused
Needs your site on Cloudflare? No — —

Turnstile is a popular choice for new projects: free, mostly invisible, and it works on any site, whether or not you use Cloudflare's other services.

How every CAPTCHA works: two halves

  1. In the browser: a widget runs its checks and produces a token — a one-time proof that the check passed. The token is submitted with your form.
  2. On your server: you send that token, plus your secret key, to the CAPTCHA provider's verification API. It replies "valid" or "invalid".

The second half is the one people forget. If your server doesn't verify the token, a bot can simply skip the widget and post directly to your form endpoint. A CAPTCHA that's only checked in the browser protects nothing.

Example: Cloudflare Turnstile

Create a widget in the Cloudflare dashboard to get a site key (public, goes in your HTML) and a secret key (private, server only).

In the form

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

<form action="/api/signup" method="POST">
  <input type="email" name="email" required>
  <div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
  <button type="submit">Sign up</button>
</form>

The widget adds a hidden field called cf-turnstile-response to the form, containing the token.

On the server

// app/api/signup/route.ts (Next.js)
export async function POST(req: Request) {
  const form = await req.formData();
  const token = form.get("cf-turnstile-response");

  const result = await fetch(
    "https://challenges.cloudflare.com/turnstile/v0/siteverify",
    {
      method: "POST",
      body: new URLSearchParams({
        secret: process.env.TURNSTILE_SECRET_KEY!,
        response: String(token ?? ""),
      }),
    }
  ).then((r) => r.json());

  if (!result.success) {
    return Response.json({ error: "Verification failed" }, { status: 400 });
  }

  // ...create the account
  return Response.json({ ok: true });
}

Notes:

  • The secret key lives in a server-side environment variable. (What is an API key?)
  • Tokens are single-use and expire after a few minutes, so verify on every submission and let the widget refresh if the user takes a long time.
  • In single-page apps, render the widget with the provider's JavaScript API or a React wrapper, and reset it after each submission.

Where to put a CAPTCHA

  • Signup — stops fake accounts.
  • Contact and feedback forms — stops spam. (How to add a contact form.)
  • Login, at least after a few failed attempts — slows password guessing.
  • Password reset and magic-link requests — stops inbox flooding and email bills.
  • Anything that costs you money per request — AI generation, SMS, email.

A CAPTCHA isn't enough on its own

CAPTCHAs raise the cost for bots; determined attackers can still get through, sometimes by paying humans to solve them. Combine them with:

Accessibility and privacy

  • Prefer invisible or "managed" modes — visual puzzles are hard or impossible for some disabled users. (Web accessibility basics.)
  • A CAPTCHA processes data about your visitors; mention it in your privacy policy.

The summary

  • A CAPTCHA gives the browser a token that your server must verify with the provider.
  • Turnstile is free and mostly invisible; reCAPTCHA and hCaptcha are alternatives.
  • Always verify the token server-side, with the secret key kept on the server.
  • Use it on signup, login, contact and anything paid — alongside rate limiting.

EasySpawn runs your backend with secrets stored as server-side environment variables, so your CAPTCHA secret stays where it belongs — and Claude Code can wire up and test the server-side check. See how it works or join the waitlist.

Related: What Is a DDoS Attack? · Form Validation Explained · How to Add Login to an AI-Built App · Magic Link Login

Keep reading