• Blog
  • Documentation
  • Courses
  • Changelog
  • AI Starters
  • UI Kit
  • FAQ
  • Supamode
    New
  • Pricing

Launch your next SaaS in record time with Makerkit, a React SaaS Boilerplate for Next.js and Supabase.

Makerkit is a product of Makerkit Pte Ltd (registered in the Republic of Singapore)Company Registration No: 202407149CFor support or inquiries, please contact us

About
  • FAQ
  • Contact
  • Verify your Discord
  • Consultation
  • Open Source
  • Become an Affiliate
Product
  • Documentation
  • Blog
  • Changelog
  • UI Blocks
  • Figma UI Kit
  • AI SaaS Starters
License
  • Activate License
  • Upgrade License
  • Invite Member
Legal
  • Terms of License
    • Supabase Clients
    • Server Actions
    • Route Handlers
    • Server Components
    • React Query
    • CSRF Protection
    • Captcha Protection

Captcha Protection for your API Routes

Learn how to set up captcha protection for your API routes.

For captcha protection, we use Cloudflare Turnstile.

How to set up captcha protection for your API routes

Learn how to set up captcha protection for your API routes

1

Setting up the environment variables

2

Enabling the captcha protection

3

Using captcha in your components

4

Verifying the token

Setting up the environment variables

To enable it, you need to set the following environment variables:

CAPTCHA_SECRET_TOKEN=
NEXT_PUBLIC_CAPTCHA_SITE_KEY=

You can find the CAPTCHA_SECRET_TOKEN in the Turnstile configuration. The NEXT_PUBLIC_CAPTCHA_SITE_KEY is public and safe to share. Instead, the CAPTCHA_SECRET_TOKEN should be kept secret.

This guide assumes you have correctly set up your Turnstile configuration. If you haven't, please refer to the https://developers.cloudflare.com/turnstile.

Enabling the captcha protection

When you set the token in the environment variables, the kit will automatically protect your API routes with captcha.

NB: you also need to set the token in the Supabase Dashboard!

Using Captcha in Your Components

The kit provides two clean APIs for captcha integration depending on your use case.

Option 1: Using the useCaptcha Hook

For auth containers and simple forms, use the useCaptcha hook for zero-boilerplate captcha integration:

import { useCaptcha } from '@kit/auth/captcha/client';
function MyComponent({ captchaSiteKey }) {
const captcha = useCaptcha({ siteKey: captchaSiteKey });
const handleSubmit = async (data) => {
try {
await myServerAction({
...data,
captchaToken: captcha.token,
});
} finally {
// Always reset after submission
captcha.reset();
}
};
return (
<form onSubmit={handleSubmit}>
{captcha.field}
<button type="submit">Submit</button>
</form>
);
}

The useCaptcha hook returns:

  • token - The current captcha token
  • reset() - Function to reset the captcha widget
  • field - The captcha component to render

Option 2: React Hook Form Integration

For forms using react-hook-form, use the CaptchaField component with automatic form integration:

import { useForm } from 'react-hook-form';
import { CaptchaField } from '@kit/auth/captcha/client';
function MyForm() {
const form = useForm({
defaultValues: {
message: '',
captchaToken: '',
},
});
const handleSubmit = async (data) => {
try {
await myServerAction(data);
form.reset(); // Automatically resets captcha too
} catch (error) {
// Handle error
}
};
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(handleSubmit)}>
{/* Your form fields */}
<CaptchaField
siteKey={config.captchaSiteKey}
control={form.control}
name="captchaToken"
/>
<button type="submit">Submit</button>
</form>
</Form>
);
}

When using React Hook Form integration:

  • The captcha token is automatically set in the form state
  • Calling form.reset() automatically resets the captcha
  • No manual state management needed

Using with Server Actions

Define your server action schema to include the captchaToken:

import { z } from 'zod';
import { enhanceAction } from '@kit/next/actions';
const MySchema = z.object({
message: z.string(),
captchaToken: z.string(),
});
export const myServerAction = enhanceAction(
async (data) => {
// Your action code - captcha is automatically verified
console.log(data.message);
},
{
captcha: true,
schema: MySchema,
}
);

When captcha: true is set, enhanceAction automatically:

  1. Extracts the captchaToken from the data
  2. Verifies it with Cloudflare Turnstile
  3. Throws an error if verification fails

Important Notes

  • Token Validity: A captcha token is valid for one request only
  • Always Reset: Always call captcha.reset() (or form.reset() with RHF) after submission, whether successful or not
  • Automatic Renewal: The library automatically renews tokens when needed, but you must reset after consumption

Verifying the Token Manually

If you need to verify the captcha token manually server-side (e.g., in API routes), use:

import { verifyCaptchaToken } from '@kit/auth/captcha/server';
async function myApiHandler(request: Request) {
const token = request.headers.get('x-captcha-token');
// Throws an error if invalid
await verifyCaptchaToken(token);
// Your API logic
}

Note: If you use enhanceAction or enhanceRouteHandler with captcha: true, verification is automatic and you don't need to call verifyCaptchaToken manually.

Migration from old API (prior to v2.18.3)

If you're migrating from the old useCaptchaToken hook:

Before:

import { useCaptchaToken } from '@kit/auth/captcha/client';
const { captchaToken, resetCaptchaToken } = useCaptchaToken();
// Manual state management required

After:

import { useCaptcha } from '@kit/auth/captcha/client';
const captcha = useCaptcha({ siteKey: captchaSiteKey });
On this page
  1. Setting up the environment variables
    1. Enabling the captcha protection
      1. Using Captcha in Your Components
        1. Option 1: Using the useCaptcha Hook
        2. Option 2: React Hook Form Integration
      2. Using with Server Actions
        1. Important Notes
      3. Verifying the Token Manually
        1. Migration from old API (prior to v2.18.3)