Files
dotfiles/.agents/skills/cloudflare-deploy/references/turnstile/README.md
2026-03-17 16:53:22 -07:00

100 lines
3.5 KiB
Markdown

# Cloudflare Turnstile Implementation Skill Reference
Expert guidance for implementing Cloudflare Turnstile - a smart CAPTCHA alternative that protects websites from bots without showing traditional CAPTCHA puzzles.
## Overview
Turnstile is a user-friendly CAPTCHA alternative that runs challenges in the background without user interaction. It validates visitors automatically using signals like browser behavior, device fingerprinting, and machine learning.
## Widget Types
| Type | Interaction | Use Case |
|------|-------------|----------|
| **Managed** (default) | Shows checkbox when needed | Forms, logins - balance UX and security |
| **Non-Interactive** | Invisible, runs automatically | Frictionless UX, low-risk actions |
| **Invisible** | Hidden, triggered programmatically | Pre-clearance, API calls, headless |
## Quick Start
### Implicit Rendering (HTML-based)
```html
<!-- 1. Add script -->
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<!-- 2. Add widget to form -->
<form action="/submit" method="POST">
<div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
<button type="submit">Submit</button>
</form>
```
### Explicit Rendering (JavaScript-based)
```html
<div id="turnstile-container"></div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"></script>
<script>
window.turnstile.render('#turnstile-container', {
sitekey: 'YOUR_SITE_KEY',
callback: (token) => console.log('Token:', token)
});
</script>
```
### Server Validation (Required)
```javascript
// Cloudflare Workers
export default {
async fetch(request) {
const formData = await request.formData();
const token = formData.get('cf-turnstile-response');
const result = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
secret: env.TURNSTILE_SECRET,
response: token,
remoteip: request.headers.get('CF-Connecting-IP')
})
});
const validation = await result.json();
if (!validation.success) {
return new Response('Invalid CAPTCHA', { status: 400 });
}
// Process form...
}
}
```
## Testing Keys
**Critical for development/testing:**
| Type | Key | Behavior |
|------|-----|----------|
| **Site Key (Always Passes)** | `1x00000000000000000000AA` | Widget succeeds, token validates |
| **Site Key (Always Blocks)** | `2x00000000000000000000AB` | Widget fails visibly |
| **Site Key (Force Challenge)** | `3x00000000000000000000FF` | Always shows interactive challenge |
| **Secret Key (Testing)** | `1x0000000000000000000000000000000AA` | Validates test tokens |
**Note:** Test keys work on `localhost` and any domain. Do NOT use in production.
## Key Constraints
- **Token expiry:** 5 minutes after generation
- **Single-use:** Each token can only be validated once
- **Server validation required:** Client-side checks are insufficient
## Reading Order
1. **[configuration.md](configuration.md)** - Setup, widget options, script loading
2. **[api.md](api.md)** - JavaScript API, siteverify endpoints, TypeScript types
3. **[patterns.md](patterns.md)** - Form integration, framework examples, validation patterns
4. **[gotchas.md](gotchas.md)** - Common errors, debugging, limitations
## See Also
- [Cloudflare Turnstile Docs](https://developers.cloudflare.com/turnstile/)
- [Dashboard](https://dash.cloudflare.com/?to=/:account/turnstile)