Skip to content
Development
Skill

/cloudflare-turnstile

This skill should be used when the user asks to \"add turnstile\", \"implement bot protection\", \"validate turnstile token\", \"fix turnstile error\", \"setup captcha alternative\", or encounters error codes 100*/300*/600*, CSP errors, or token validation failures. Provides

From plugin
secondsky-claude-skills
219183 skills42 agents62 commands2 MCP
Install
$ npx -y skills add secondsky/claude-skills --skill cloudflare-turnstile --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/cloudflare-turnstile

Context preview

The summary Claude sees to decide when to auto-load this skill.

This skill should be used when the user asks to \"add turnstile\", \"implement bot protection\", \"validate turnstile token\", \"fix turnstile error\", \"setup captcha alternative\", or encounters error codes 100*/300*/600*, CSP errors, or token validation failures. Provides

SKILL.md

cloudflare-turnstile.SKILL.md
name: cloudflare-turnstile
description: "This skill should be used when the user asks to \"add turnstile\", \"implement bot protection\", \"validate turnstile token\", \"fix turnstile error\", \"setup captcha alternative\", or encounters error codes 100*/300*/600*, CSP errors, or token validation failures. Provides CAPTCHA-alternative protection for Cloudflare Workers, React, Next.js, and Hono."
license: MIT
metadata:
  version: "1.1.0"
  last_verified: "2025-11-26"
  react_turnstile_version: "1.3.1"
  turnstile_types_version: "1.2.3"
  errors_prevented: 12
  templates_included: 7
  references_included: 8
  keywords:
    - turnstile
    - captcha
    - bot protection
    - cloudflare challenge
    - siteverify
    - recaptcha alternative
    - spam prevention
    - form protection
    - cf-turnstile
    - turnstile widget
    - token validation
    - managed challenge
    - invisible challenge
    - "@marsidev/react-turnstile"
    - hono turnstile
    - workers turnstile

Cloudflare Turnstile

**Status**: Production Ready ✅ | **Last Verified**: 2025-11-26

**Dependencies**: None (optional: @marsidev/react-turnstile for React)

**Contents**: [Quick Start](#quick-start-10-minutes) • [Critical Rules](#critical-rules) • [Top 12 Errors](#known-issues-prevention) • [Common Patterns](#common-patterns) • [When to Load References](#when-to-load-references) • [Troubleshooting](#troubleshooting)

---

Quick Start (10 Minutes)

1. Create Turnstile Widget

Get your sitekey and secret key from Cloudflare Dashboard.

# Navigate to: https://dash.cloudflare.com/?to=/:account/turnstile
# Create new widget → Copy sitekey (public) and secret key (private)

**Why this matters:**

  • Each widget has unique sitekey/secret pair
  • Sitekey goes in frontend (public)
  • Secret key ONLY in backend (private)
  • Use different widgets for dev/staging/production

2. Add Widget to Frontend

Embed the Turnstile widget in your HTML form.

<!DOCTYPE html>
<html>
<head>
  <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</head>
<body>
  <form id="myForm" action="/submit" method="POST">
    <input type="email" name="email" required>
    <!-- Turnstile widget renders here -->
    <div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
    <button type="submit">Submit</button>
  </form>
</body>
</html>

**CRITICAL:**

  • Never proxy or cache `api.js` - must load from Cloudflare CDN
  • Widget auto-creates hidden input `cf-turnstile-response` with token
  • Token expires in 5 minutes
  • Each token is single-use only

3. Validate Token on Server

ALWAYS validate the token server-side. Client-side verification alone is not secure.

// Cloudflare Workers example
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const formData = await request.formData()
    const token = formData.get('cf-turnstile-response')
    const ip = request.headers.get('CF-Connecting-IP')

    // Validate token with Siteverify API
    const verifyFormData = new FormData()
    verifyFormData.append('secret', env.TURNSTILE_SECRET_KEY)
    verifyFormData.append('response', token)
    verifyFormData.append('remoteip', ip)

    const result = await fetch(
      'https://challenges.cloudflare.com/turnstile/v0/siteverify',
      {
        method: 'POST',
        body: verifyFormData,
      }
    )

    const outcome = await result.json()

    if (!outcome.success) {
      return new Response('Invalid Turnstile token', { status: 401 })
    }

    // Token valid - proceed with form processing
    return new Response('Success!')
  }
}

---

The 3-Step Setup Process

Step 1: Create Widget Configuration

1. Log into Cloudflare Dashboard 2. Navigate to Turnstile section 3. Click "Add Site" 4. Configure:

  • **Widget Mode**: Managed (recommended), Non-Interactive, or Invisible
  • **Domains**: Add allowed hostnames (e.g., example.com, localhost for dev)
  • **Name**: Descriptive name (e.g., "Production Login Form")

**Key Points:**

  • Use separate widgets for dev/staging/production
  • Restrict domains to only those you control
  • Managed mode provides best balance of security and UX
  • localhost must be explicitly added for local testing

Step 2: Client-Side Integration

Choose between implicit or explicit rendering:

**Implicit Rendering** (Recommended for static forms):

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

<!-- 2. Add widget -->
<div class="cf-turnstile"
     data-sitekey="YOUR_SITE_KEY"
     data-callback="onSuccess"
     data-error-callback="onError"></div>

<script>
function onSuccess(token) {
  console.log('Turnstile success:', token)
}

function onError(error) {
  console.error('Turnstile error:', error)
}
</script>

**Explicit Rendering** (For SPAs/dynamic UIs):

// 1. Load script with explicit mode
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" defer></script>

// 2. Render programmatically
const widgetId = turnstile.render('#container', {
  sitekey: 'YOUR_SITE_KEY',
  callback: (token) => {
    console.log('Token:', token)
  },
  'error-callback': (error) => {
    console.error('Error:', error)
  },
  theme: 'auto',
  execution: 'render', // or 'execute' for manual trigger
})

// Control lifecycle
turnstile.reset(widgetId)        // Reset widget
turnstile.remove(widgetId)       // Remove widget
turnstile.execute(widgetId)      // Manually trigger challenge
const token = turnstile.getResponse(widgetId) // Get current token

**React Integration** (using @marsidev/react-turnstile):

import { Turnstile } from '@marsidev/react-turnstile'

export function MyForm() {
  const [token, setToken] = useState<string>()

  return (
    <form>
      <Turnstile
        siteKey={TURNSTILE_SITE_KEY}
        onSuccess={setToken}
        onError={(error) => console.error(error)}
      />
Read more
Ships withsecondsky-claude-skills

145 production-ready skills for Claude Code CLI 🔌 Platform / Harness Support These plugins ship as Claude Code marketplace plugins (.claude-plugin/ manifests) and Codex CLI plugins (.codex-plugin/ manifests).

Get the whole plugin

Other skills on secondsky-claude-skills.