
cloudflare-turnstile
Add bot protection with Turnstile (CAPTCHA alternative). Use when: protecting forms, securing login/signup, preventing spam, migrating from reCAPTCHA, integrating with React/Next.js/Hono, implementing E2E tests, or debugging CSP errors, token validation failures, or error codes 100/300/600.
- Installs
- 70
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/ovachiever/droid-tings --skill cloudflare-turnstile- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads cloudflare-turnstile automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Cloudflare Turnstile
Status: Production Ready Last Updated: 2025-10-22 Dependencies: None (optional: @marsidev/react-turnstile for React) Latest Versions: @marsidev/react-turnstile@1.3.1, turnstile-types@1.2.3
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-responsewith 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
- Log into Cloudflare Dashboard
- Navigate to Turnstile section
- Click "Add Site"
- 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)}
/>
<button disabled={!token}>Submit</button>
</form>
)
}
Step 3: Server-Side Validation
MANDATORY: Always call Siteverify API to validate tokens.
interface TurnstileResponse {
success: boolean
challenge_ts?: string
hostname?: string
error-codes?: string[]
action?: string
cdata?: string
}
async function validateTurnstile(
token: string,
secretKey: string,
options?: {
remoteip?: string
idempotency_key?: string
expectedAction?: string
expectedHostname?: string
}
): Promise<TurnstileResponse> {
const formData = new FormData()
formData.append('secret', secretKey)
formData.append('response', token)
if (options?.remoteip) {
formData.append('remoteip', options.remoteip)
}
if (options?.idempotency_key) {
formData.append('idempotency_key', options.idempotency_key)
}
const response = await fetch(
'https://challenges.cloudflare.com/turnstile/v0/siteverify',
{
method: 'POST',
body: formData,
}
)
const result = await response.json<TurnstileResponse>()
// Additional validation
if (result.success) {
if (options?.expectedAction && result.action !== options.expectedAction) {
return { success: false, 'error-codes': ['action-mismatch'] }
}
if (options?.expectedHostname && result.hostname !== options.expectedHostname) {
return { success: false, 'error-codes': ['hostname-mismatch'] }
}
}
return result
}
// Usage in Cloudflare Worker
const result = await validateTurnstile(
token,
env.TURNSTILE_SECRET_KEY,
{
remoteip: request.headers.get('CF-Connecting-IP'),
expectedHostname: 'example.com',
}
)
if (!result.success) {
return new Response('Turnstile validation failed', { status: 401 })
}
Critical Rules
Always Do
✅ Call Siteverify API - Server-side validation is mandatory
✅ Use HTTPS - Never validate over HTTP
✅ Protect secret keys - Never expose in frontend code
✅ Handle token expiration - Tokens expire after 5 minutes
✅ Implement error callbacks - Handle failures gracefully
✅ Use dummy keys for testing - Test sitekey: 1x00000000000000000000AA
✅ Set reasonable timeouts - Don't wait indefinitely for validation
✅ Validate action/hostname - Check additional fields when specified
✅ Rotate keys periodically - Use dashboard or API to rotate secrets
✅ Monitor analytics - Track solve rates and failures
Never Do
❌ Skip server validation - Client-side only = security vulnerability ❌ Proxy api.js script - Must load from Cloudflare CDN ❌ Reuse tokens - Each token is single-use only ❌ Use GET requests - Siteverify only accepts POST ❌ Expose secret key - Keep secrets in backend environment only ❌ Trust client-side validation - Tokens can be forged ❌ Cache api.js - Future updates will break your integration ❌ Use production keys in tests - Use dummy keys instead ❌ Ignore error callbacks - Always handle failures
Known Issues Prevention
This skill prevents 12 documented issues:
Issue #1: Missing Server-Side Validation
Error: Zero token validation in Turnstile Analytics dashboard Source: https://developers.cloudflare.com/turnstile/get-started/ Why It Happens: Developers only implement client-side widget, skip Siteverify call Prevention: All templates include mandatory server-side validation with Siteverify API
Issue #2: Token Expiration (5 Minutes)
Error: success: false for valid tokens submitted after delay
Source: https://developers.cloudflare.com/turnstile/get-started/server-side-validation
Why It Happens: Tokens expire 300 seconds after generation
Prevention: Templates document TTL and implement token refresh on expiration
Issue #3: Secret Key Exposed in Frontend
Error: Security bypass - attackers can validate their own tokens Source: https://developers.cloudflare.com/turnstile/get-started/server-side-validation Why It Happens: Secret key hardcoded in JavaScript or visible in source Prevention: All templates show backend-only validation with environment variables
Issue #4: GET Request to Siteverify
Error: API returns 405 Method Not Allowed Source: https://developers.cloudflare.com/turnstile/migration/recaptcha Why It Happens: reCAPTCHA supports GET, Turnstile requires POST Prevention: Templates use POST with FormData or JSON body
Issue #5: Content Security Policy Blocking
Error: Error 200500 - "Loading error: The iframe could not be loaded" Source: https://developers.cloudflare.com/turnstile/troubleshooting/client-side-errors/error-codes Why It Happens: CSP blocks challenges.cloudflare.com iframe Prevention: Skill includes CSP configuration reference and check-csp.sh script
Issue #6: Widget Crash (Error 300030)
Error: Generic client execution error for legitimate users Source: https://community.cloudflare.com/t/turnstile-is-frequently-generating-300x-errors/700903 Why It Happens: Unknown - appears to be Cloudflare-side issue (2025) Prevention: Templates implement error callbacks, retry logic, and fallback handling
Issue #7: Configuration Error (Error 600010)
Error: Widget fails with "configuration error" Source: https://community.cloudflare.com/t/repeated-cloudflare-turnstile-error-600010/644578 Why It Happens: Missing or deleted hostname in widget configuration Prevention: Templates document hostname allowlist requirement and verification steps
Issue #8: Safari 18 / macOS 15 "Hide IP" Issue
Error: Error 300010 when Safari's "Hide IP address" is enabled Source: https://community.cloudflare.com/t/turnstile-is-frequently-generating-300x-errors/700903 Why It Happens: Privacy settings interfere with challenge signals Prevention: Error handling reference documents Safari workaround (disable Hide IP)
Issue #9: Brave Browser Confetti Animation Failure
Error: Verification fails during success animation Source: https://github.com/brave/brave-browser/issues/45608 (April 2025) Why It Happens: Brave shields block animation scripts Prevention: Templates handle success before animation completes
Issue #10: Next.js + Jest Incompatibility
Error: @marsidev/react-turnstile breaks Jest tests Source: https://github.com/marsidev/react-turnstile/issues/112 (Oct 2025) Why It Happens: Module resolution issues with Jest Prevention: Testing guide includes Jest mocking patterns and dummy sitekey usage
Issue #11: localhost Not in Allowlist
Error: Error 110200 - "Unknown domain: Domain not allowed" Source: https://developers.cloudflare.com/turnstile/troubleshooting/client-side-errors/error-codes Why It Happens: Production widget used in development without localhost in allowlist Prevention: Templates use dummy test keys for dev, document localhost allowlist requirement
Issue #12: Token Reuse Attempt
Error: success: false with "token already spent" error
Source: https://developers.cloudflare.com/turnstile/troubleshooting/testing
Why It Happens: Each token can only be validated once
Prevention: Templates document single-use constraint and token refresh patterns
Configuration Files Reference
wrangler.jsonc (Cloudflare Workers)
{
"name": "my-app",
"main": "src/index.ts",
"compatibility_date": "2025-10-22",
// Public sitekey (safe to commit)
"vars": {
"TURNSTILE_SITE_KEY": "1x00000000000000000000AA" // Use real key in production
},
// Secret key (DO NOT commit - use wrangler secret)
// Run: wrangler secret put TURNSTILE_SECRET_KEY
"secrets": ["TURNSTILE_SECRET_KEY"]
}
Why these settings:
varsfor public sitekey (visible in client code)secretsfor private secret key (encrypted, backend-only)- Use dummy keys for development (see testing-guide.md)
- Rotate production secret keys quarterly
Required CSP Directives
<meta http-equiv="Content-Security-Policy" content="
script-src 'self' https://challenges.cloudflare.com;
frame-src 'self' https://challenges.cloudflare.com;
connect-src 'self' https://challenges.cloudflare.com;
">
Common Patterns
Pattern 1: Hono + Cloudflare Workers
import { Hono } from 'hono'
type Bindings = {
TURNSTILE_SECRET_KEY: string
TURNSTILE_SITE_KEY: string
}
const app = new Hono<{ Bindings: Bindings }>()
app.post('/api/login', async (c) => {
const body = await c.req.formData()
const token = body.get('cf-turnstile-response')
if (!token) {
return c.text('Missing Turnstile token', 400)
}
// Validate token
const verifyFormData = new FormData()
verifyFormData.append('secret', c.env.TURNSTILE_SECRET_KEY)
verifyFormData.append('response', token.toString())
verifyFormData.append('remoteip', c.req.header('CF-Connecting-IP') || '')
const verifyResult = await fetch(
'https://challenges.cloudflare.com/turnstile/v0/siteverify',
{
method: 'POST',
body: verifyFormData,
}
)
const outcome = await verifyResult.json<{ success: boolean }>()
if (!outcome.success) {
return c.text('Invalid Turnstile token', 401)
}
// Process login
return c.json({ message: 'Login successful' })
})
export default app
When to use: API routes in Cloudflare Workers with Hono framework
Pattern 2: React + Next.js App Router
'use client'
import { Turnstile } from '@marsidev/react-turnstile'
import { useState } from 'react'
export function ContactForm() {
const [token, setToken] = useState<string>()
const [error, setError] = useState<string>()
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
if (!token) {
setError('Please complete the challenge')
return
}
const formData = new FormData(e.currentTarget)
formData.append('cf-turnstile-response', token)
const response = await fetch('/api/contact', {
method: 'POST',
body: formData,
})
if (!response.ok) {
setError('Submission failed')
return
}
// Success
}
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" required />
<textarea name="message" required />
<Turnstile
siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY!}
onSuccess={setToken}
onError={() => setError('Challenge failed')}
onExpire={() => setToken(undefined)}
/>
{error && <div className="error">{error}</div>}
<button type="submit" disabled={!token}>
Submit
</button>
</form>
)
}
When to use: Client-side forms in Next.js with React hooks
Pattern 3: E2E Testing with Dummy Keys
// test/helpers/turnstile.ts
export const TEST_TURNSTILE = {
sitekey: {
alwaysPass: '1x00000000000000000000AA',
alwaysBlock: '2x00000000000000000000AB',
invisible: '1x00000000000000000000BB',
interactive: '3x00000000000000000000FF',
},
secretKey: {
alwaysPass: '1x0000000000000000000000000000000AA',
alwaysFail: '2x0000000000000000000000000000000AA',
tokenSpent: '3x0000000000000000000000000000000AA',
},
dummyToken: 'XXXX.DUMMY.TOKEN.XXXX',
}
// Playwright test example
test('form submission with Turnstile', async ({ page }) => {
// Set test environment
await page.goto('/contact?test=true')
// Widget uses test sitekey in test mode
await page.fill('input[name="email"]', 'test@example.com')
// Turnstile auto-solves with dummy token
await page.click('button[type="submit"]')
await expect(page.locator('.success')).toBeVisible()
})
When to use: Automated testing (Playwright, Cypress, Jest)
Pattern 4: Widget Lifecycle Management
class TurnstileManager {
private widgetId: string | null = null
private sitekey: string
constructor(sitekey: string) {
this.sitekey = sitekey
}
render(containerId: string, callbacks: {
onSuccess: (token: string) => void
onError: (error: string) => void
}) {
if (this.widgetId !== null) {
this.reset() // Reset if already rendered
}
this.widgetId = turnstile.render(containerId, {
sitekey: this.sitekey,
callback: callbacks.onSuccess,
'error-callback': callbacks.onError,
'expired-callback': () => this.reset(),
})
return this.widgetId
}
reset() {
if (this.widgetId !== null) {
turnstile.reset(this.widgetId)
}
}
remove() {
if (this.widgetId !== null) {
turnstile.remove(this.widgetId)
this.widgetId = null
}
}
getToken(): string | undefined {
if (this.widgetId === null) return undefined
return turnstile.getResponse(this.widgetId)
}
}
// Usage
const manager = new TurnstileManager(SITE_KEY)
manager.render('#container', {
onSuccess: (token) => console.log('Token:', token),
onError: (error) => console.error('Error:', error),
})
When to use: SPAs requiring programmatic widget control
Using Bundled Resources
Scripts (scripts/)
- check-csp.sh - Verifies Content Security Policy allows Turnstile scripts and iframes
Example Usage:
./scripts/check-csp.sh https://example.com
References (references/)
references/widget-configs.md- Complete reference of all widget configuration optionsreferences/error-codes.md- Comprehensive error code reference with troubleshootingreferences/testing-guide.md- Testing strategies, dummy keys, E2E patternsreferences/react-integration.md- React-specific patterns and @marsidev/react-turnstile usage
When Claude should load these:
widget-configs.md: When configuring widget appearance, themes, or execution modeserror-codes.md: When debugging error codes 100*, 200*, 300*, 400*, 600*testing-guide.md: When setting up E2E tests or local developmentreact-integration.md: When integrating with React, Next.js, or encountering React-specific issues
Templates (templates/)
wrangler-turnstile-config.jsonc- Cloudflare Workers environment configurationturnstile-widget-implicit.html- Implicit rendering HTML exampleturnstile-widget-explicit.ts- Explicit rendering JavaScript APIturnstile-server-validation.ts- Siteverify API validation functionturnstile-react-component.tsx- React component using @marsidev/react-turnstileturnstile-hono-route.ts- Hono route handler with validationturnstile-test-config.ts- Testing configuration with dummy keys
Advanced Topics
Pre-Clearance for SPAs
Turnstile can issue a pre-clearance cookie that persists across page navigations in single-page applications.
turnstile.render('#container', {
sitekey: SITE_KEY,
callback: async (token) => {
// Request pre-clearance cookie
await fetch('/api/pre-clearance', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token }),
})
},
})
Custom Actions and cData
Track different challenge types or pass custom data:
turnstile.render('#container', {
sitekey: SITE_KEY,
action: 'login', // Track action in analytics
cdata: JSON.stringify({ userId: '123' }), // Custom data (max 255 chars)
callback: (token) => {
// Token includes action and cdata for server validation
},
})
Server-side verification:
const result = await validateTurnstile(token, secretKey)
if (result.action !== 'login') {
return new Response('Invalid action', { status: 400 })
}
const customData = JSON.parse(result.cdata || '{}')
Retry and Error Handling Strategies
class TurnstileWithRetry {
private retryCount = 0
private maxRetries = 3
render(containerId: string) {
turnstile.render(containerId, {
sitekey: SITE_KEY,
retry: 'auto', // or 'never' for manual control
'retry-interval': 8000, // ms between retries
'error-callback': (error) => {
this.handleError(error)
},
})
}
private handleError(error: string) {
// Error codes that should not retry
const noRetry = ['110100', '110200', '110500']
if (noRetry.some(code => error.includes(code))) {
this.showFallback()
return
}
// Retry on transient errors
if (this.retryCount < this.maxRetries) {
this.retryCount++
setTimeout(() => {
turnstile.reset(this.widgetId)
}, 2000 * this.retryCount) // Exponential backoff
} else {
this.showFallback()
}
}
private showFallback() {
// Show alternative verification method
console.error('Turnstile failed - showing fallback')
}
}
Multi-Widget Pages
const widgets = {
login: null as string | null,
signup: null as string | null,
}
// Render multiple widgets
widgets.login = turnstile.render('#login-widget', {
sitekey: SITE_KEY,
action: 'login',
})
widgets.signup = turnstile.render('#signup-widget', {
sitekey: SITE_KEY,
action: 'signup',
})
// Reset specific widget
turnstile.reset(widgets.login)
// Get token from specific widget
const loginToken = turnstile.getResponse(widgets.login)
Dependencies
Required:
- None (Turnstile loads from CDN)
Optional (React):
@marsidev/react-turnstile@1.3.1- Official Cloudflare-recommended React integrationturnstile-types@1.2.3- TypeScript type definitions
Optional (Other Frameworks):
vue-turnstile- Vue 3 integrationcfturnstile-vue3- Alternative Vue 3 wrapperngx-turnstile- Angular integrationsvelte-turnstile- Svelte integration@nuxtjs/turnstile- Nuxt full-stack integration
Official Documentation
- Cloudflare Turnstile: https://developers.cloudflare.com/turnstile/
- Get Started: https://developers.cloudflare.com/turnstile/get-started/
- Client-Side Rendering: https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/
- Server-Side Validation: https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
- Error Codes: https://developers.cloudflare.com/turnstile/troubleshooting/client-side-errors/error-codes/
- Testing: https://developers.cloudflare.com/turnstile/troubleshooting/testing/
- Community Resources: https://developers.cloudflare.com/turnstile/community-resources/
- Migration from reCAPTCHA: https://developers.cloudflare.com/turnstile/migration/recaptcha/
- Cloudflare MCP: Use
mcp__cloudflare-docs__search_cloudflare_documentationtool
Package Versions (Verified 2025-10-22)
{
"devDependencies": {
"@marsidev/react-turnstile": "^1.3.1",
"turnstile-types": "^1.2.3"
}
}
Notes:
- @marsidev/react-turnstile is Cloudflare's recommended React package
- Last updated September 2025 (actively maintained)
- Compatible with React 18+, Next.js 13+, Next.js 14+, Next.js 15+
Production Example
This skill is based on production implementations:
- Cloudflare Workers: Official HTMLRewriter example
- React Apps: @marsidev/react-turnstile (Cloudflare-verified)
- Community: WordPress, Craft CMS, SilverStripe, Statamic integrations
- Validation: ✅ All 12 known issues documented and prevented
Troubleshooting
Problem: Error 110200 - "Unknown domain"
Solution: Add your domain (including localhost for dev) to widget's allowed domains in Cloudflare Dashboard. For local dev, use dummy test sitekey 1x00000000000000000000AA instead.
Problem: Error 300030 - Widget crashes for legitimate users
Solution: Implement error callback with retry logic. This is a known Cloudflare-side issue (2025). Fallback to alternative verification if retries fail.
Problem: Tokens always return success: false
Solution:
- Check token hasn't expired (5 min TTL)
- Verify secret key is correct
- Ensure token hasn't been validated before (single-use)
- Check hostname matches widget configuration
Problem: CSP blocking iframe (Error 200500)
Solution: Add CSP directives:
<meta http-equiv="Content-Security-Policy" content="
frame-src https://challenges.cloudflare.com;
script-src https://challenges.cloudflare.com;
">
Problem: Safari 18 "Hide IP" causing Error 300010
Solution: Document in error message that users should disable Safari's "Hide IP address" setting (Safari → Settings → Privacy → Hide IP address → Off)
Problem: Next.js + Jest tests failing with @marsidev/react-turnstile
Solution: Mock the Turnstile component in Jest setup:
// jest.setup.ts
jest.mock('@marsidev/react-turnstile', () => ({
Turnstile: () => <div data-testid="turnstile-mock" />,
}))
Complete Setup Checklist
Use this checklist to verify your setup:
- Created Turnstile widget in Cloudflare Dashboard
- Added allowed domains (including localhost for dev)
- Frontend widget loads from
https://challenges.cloudflare.com/turnstile/v0/api.js - Widget renders with correct sitekey
- Error callback implemented and tested
- Server-side Siteverify validation implemented
- Secret key stored in environment variable (not hardcoded)
- Token validation includes remoteip check
- CSP allows challenges.cloudflare.com (if using CSP)
- Testing uses dummy sitekeys (
1x00000000000000000000AA) - Token expiration handling implemented (5 min TTL)
- Widget accessibility tested (keyboard navigation, screen readers)
- Error states display user-friendly messages
- Production deployment uses separate widget from dev/staging
Questions? Issues?
- Check
references/error-codes.mdfor specific error troubleshooting - Verify all steps in the 3-Step Setup Process
- Check official docs: https://developers.cloudflare.com/turnstile/
- Ensure server-side validation is implemented (most common issue)
- Use Cloudflare Docs MCP tool:
mcp__cloudflare-docs__search_cloudflare_documentation
Token Efficiency: ~65-70% savings (10-12k tokens → 3-4k tokens)
Errors Prevented: 12 documented issues with complete solutions
FAQ
What does the cloudflare-turnstile skill do?
Add bot protection with Turnstile (CAPTCHA alternative). Use when: protecting forms, securing login/signup, preventing spam, migrating from reCAPTCHA, integrating with React/Next.js/Hono, implementing E2E tests, or debugging CSP errors, token validation failures, or error codes 100/300/600.
How do I install cloudflare-turnstile?
Run npx skills add https://github.com/ovachiever/droid-tings --skill cloudflare-turnstile in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/cloudflare-turnstile/ (personal) or .claude/skills/cloudflare-turnstile/ (project).
Which AI agents can use cloudflare-turnstile?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is cloudflare-turnstile safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains cloudflare-turnstile?
It is published by ovachiever in ovachiever/droid-tings. It has 70 recorded installs.
More from ovachiever/droid-tings
security-auditor
ovachiever/droid-tings
Continuous security vulnerability scanning for OWASP Top 10, common vulnerabilities, and insecure patterns. Use when reviewing code, before deployments, or on file changes. Scans for SQL injection, XSS, secrets exposure, auth issues. Triggers on file changes, security mentions, deployment prep.
react-hook-form-zod
ovachiever/droid-tings
Build type-safe validated forms in React using React Hook Form and Zod schema validation. Single schema works on both client and server for DRY validation with full TypeScript type inference via z.infer. Use when: building forms with validation, integrating shadcn/ui Form components, implementing multi-step wizards, handling dynamic field arrays with useFieldArray, or fixing uncontrolled to controlled warnings, resolver errors, async validation issues.
deep-reading-analyst
ovachiever/droid-tings
Comprehensive framework for deep analysis of articles, papers, and long-form content using 10+ thinking models (SCQA, 5W2H, critical thinking, inversion, mental models, first principles, systems thinking, six thinking hats). Use when users want to: (1) deeply understand complex articles/content, (2) analyze arguments and identify logical flaws, (3) extract actionable insights from reading materials, (4) create study notes or learning summaries, (5) compare multiple sources, (6) transform knowledge into practical applications, or (7) apply specific thinking frameworks. Triggered by phrases like 'analyze this article,' 'help me understand,' 'deep dive into,' 'extract insights from,' 'use [framework name],' or when users provide URLs/long-form content for analysis.
nextjs-shadcn-builder
ovachiever/droid-tings
Build new Next.js applications or migrate existing frontends (React, Vue, Angular, vanilla JS, etc.) to Next.js + shadcn/ui with systematic analysis and conversion. Enforces shadcn design principles - CSS variables for theming, standard UI components, no hardcoded values, consistent typography/colors. Use for creating Next.js apps, migrating frontends, adopting shadcn/ui, or standardizing component libraries. Includes MCP integration for shadcn documentation and automated codebase analysis.
playwright browser automation
ovachiever/droid-tings
Complete browser automation with Playwright. Auto-detects dev servers, writes clean test scripts to /tmp. Test pages, fill forms, take screenshots, check responsive design, validate UX, test login flows, check links, automate any browser task. Use when user wants to test websites, automate browser interactions, validate web functionality, or perform any browser-based testing.
reportlab
ovachiever/droid-tings
PDF generation toolkit. Create invoices, reports, certificates, forms, charts, tables, barcodes, QR codes, Canvas/Platypus APIs, for professional document automation.
Popular devops & cloud skills
microsoft-foundry
microsoft/azure-skills
Build, deploy, evaluate, optimize, fine-tune, and manage Microsoft Foundry agents, models, and resources end to end. USE FOR: foundry, azd ai agent, azd provision/deploy, hosted agent scaffold/develop/run/deploy/troubleshoot, prompt agent create, managed harness agent create, GitHub Copilot harness agent, create agent, update agent, add tool to agent, invoke agent, agent insights, evaluate agent, batch eval, continuous eval, continuous monitoring, agent CI/CD, optimize prompt, improve prompt, optimize agent instructions, Agent Optimizer scaffold, dataset curation from traces, deploy model, model fine-tuning (SFT/DPO/RFT), Foundry project, RBAC, role assignment, permissions, quota, capacity, region, deployment failure, AI Services, create Foundry resource, knowledge index, customize deployment, onboard, availability, training-data, grader, distillation, large file upload. DO NOT USE FOR: Azure Functions, App Service, general Azure deploy (use azure-deploy), general Azure prep (use azure
azure-diagnostics
microsoft/azure-skills
Debug Azure production issues on Azure using AppLens, Azure Monitor, resource health, and safe triage. WHEN: debug production issues, troubleshoot app service, app service high CPU, app service deployment failure, troubleshoot container apps, container app 503 or target port mismatch, troubleshoot functions, function telemetry missing or stopped after a config/connection-string change, troubleshoot AKS, VM RDP, Linux SSH, VM black screen, can't connect to VM, reset VM password, NSG or firewall blocking, kubectl cannot connect, kube-system/CoreDNS failures, pod pending, crashloop, node not ready, upgrade failures, analyze logs, KQL, insights, image pull failures, cold start issues, health probe failures, resource health, root cause of errors, troubleshoot event hubs, troubleshoot service bus, messaging SDK error, AMQP connection failure, message lock lost, lock renewal during long-running processing, service bus dead letter.
azure-prepare
microsoft/azure-skills
Prepare azd-based Azure projects for deployment: generates azure.yaml, infrastructure (Bicep/Terraform), and Dockerfiles for the Azure Developer CLI (azd) workflow. USE ONLY when the user explicitly wants to use azd as the deployment tool, or the project already has an azure.yaml file. DO NOT USE FOR: non-azd deployments, Python App Service code-only deploys (use python-appservice-deploy), or cross-cloud migration (use azure-cloud-migrate). WHEN: prepare app for azd, create azure.yaml, set up azd infrastructure, modernize app for Azure with azd, deploy with azd, function app, timer trigger, service bus trigger, event-driven function, managed identity, generate Bicep, generate Terraform, create and deploy to Azure.
azure-ai
microsoft/azure-skills
Use for Azure AI: Search, Speech, OpenAI, Document Intelligence. Helps with search, vector/hybrid search, speech-to-text, text-to-speech, transcription, OCR. WHEN: AI Search, query search, vector search, hybrid search, semantic search, speech-to-text, text-to-speech, transcribe, OCR, convert text to speech.
azure-deploy
microsoft/azure-skills
Execute Azure deployments for ALREADY-PREPARED applications that have existing .azure/deployment-plan.md and infrastructure files. DO NOT use this skill when the user asks to CREATE a new application — use azure-prepare instead. This skill runs azd up, azd deploy, terraform apply, and az deployment commands with built-in error recovery. Requires .azure/deployment-plan.md from azure-prepare and validated status from azure-validate. WHEN: "run azd up", "run azd deploy", "execute deployment", "push to production", "push to cloud", "go live", "ship it", "bicep deploy", "terraform apply", "publish to Azure", "launch on Azure". DO NOT USE WHEN: "create and deploy", "build and deploy", "create a new app", "set up infrastructure", "create and deploy to Azure using Terraform" — use azure-prepare for these.
azure-validate
microsoft/azure-skills
Pre-deployment validation for Azure readiness. Run deep checks on configuration, infrastructure (Bicep or Terraform), RBAC role assignments, managed identity permissions, and prerequisites before deploying. WHEN: validate my app, check deployment readiness, run preflight checks, verify configuration, check if ready to deploy, validate azure.yaml, validate Bicep, test before deploying, troubleshoot deployment errors, validate Azure Functions, validate function app, validate serverless deployment, verify RBAC roles, check role assignments, review managed identity permissions, what-if analysis, validate Container Apps deployment.