名称: cloudflare-turnstile 描述: 此技能应在用户请求“添加turnstile”、“实现机器人防护”、“验证turnstile令牌”、“修复turnstile错误”、“设置captcha替代”或遇到错误代码100*/300*/600*、CSP错误或令牌验证失败时使用。为Cloudflare Workers、React、Next.js和Hono提供CAPTCHA替代防护。
关键词: turnstile, captcha, 机器人防护, cloudflare挑战, siteverify, recaptcha替代, 垃圾邮件预防, 表单防护, cf-turnstile, turnstile小部件, 令牌验证, 托管挑战, 无形挑战, @marsidev/react-turnstile, hono turnstile, workers turnstile 许可证: MIT 元数据: 版本: “1.1.0” 最后验证: “2025-11-26” react_turnstile版本: “1.3.1” turnstile_types版本: “1.2.3” 预防错误: 12 包含模板: 7 包含参考: 8
Cloudflare Turnstile
状态: 生产就绪 ✅ | 最后验证: 2025-11-26
依赖项: 无(可选: @marsidev/react-turnstile 用于React)
内容: 快速开始 • 关键规则 • 前12个错误 • 常见模式 • 何时加载参考 • 故障排除
快速开始 (10 分钟)
1. 创建 Turnstile 小部件
从Cloudflare仪表板获取您的站点密钥和秘密密钥。
# 导航到: https://dash.cloudflare.com/?to=/:account/turnstile
# 创建新小部件 → 复制站点密钥(公共)和秘密密钥(私有)
为什么重要:
- 每个小部件有唯一的站点密钥/秘密对
- 站点密钥用于前端(公共)
- 秘密密钥仅用于后端(私有)
- 为开发/暂存/生产使用不同小部件
2. 将小部件添加到前端
在HTML表单中嵌入Turnstile小部件。
<!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小部件在此渲染 -->
<div class="cf-turnstile" data-sitekey="您的站点密钥"></div>
<button type="submit">提交</button>
</form>
</body>
</html>
关键:
- 切勿代理或缓存
api.js- 必须从Cloudflare CDN加载 - 小部件自动创建隐藏输入
cf-turnstile-response包含令牌 - 令牌在5分钟后过期
- 每个令牌仅单次使用
3. 在服务器上验证令牌
始终在服务器端验证令牌。仅客户端验证不安全。
// Cloudflare Workers示例
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')
// 使用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('无效的Turnstile令牌', { status: 401 })
}
// 令牌有效 - 继续表单处理
return new Response('成功!')
}
}
三步设置流程
步骤1: 创建小部件配置
- 登录Cloudflare仪表板
- 导航到Turnstile部分
- 点击“添加站点”
- 配置:
- 小部件模式: 托管(推荐)、非交互或无形
- 域名: 添加允许的主机名(如example.com,localhost用于开发)
- 名称: 描述性名称(如“生产登录表单”)
关键点:
- 为开发/暂存/生产使用单独小部件
- 将域名限制为您控制的域名
- 托管模式提供安全性和用户体验的最佳平衡
- localhost必须明确添加用于本地测试
步骤2: 客户端集成
选择隐式或显式渲染:
隐式渲染(推荐用于静态表单):
<!-- 1. 加载脚本 -->
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<!-- 2. 添加小部件 -->
<div class="cf-turnstile"
data-sitekey="您的站点密钥"
data-callback="onSuccess"
data-error-callback="onError"></div>
<script>
function onSuccess(token) {
console.log('Turnstile成功:', token)
}
function onError(error) {
console.error('Turnstile错误:', error)
}
</script>
显式渲染(用于SPA/动态UI):
// 1. 加载脚本带显式模式
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" defer></script>
// 2. 编程渲染
const widgetId = turnstile.render('#container', {
sitekey: '您的站点密钥',
callback: (token) => {
console.log('令牌:', token)
},
'error-callback': (error) => {
console.error('错误:', error)
},
theme: 'auto',
execution: 'render', // 或 'execute' 用于手动触发
})
// 控制生命周期
turnstile.reset(widgetId) // 重置小部件
turnstile.remove(widgetId) // 移除小部件
turnstile.execute(widgetId) // 手动触发挑战
const token = turnstile.getResponse(widgetId) // 获取当前令牌
React集成(使用@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}>提交</button>
</form>
)
}
步骤3: 服务器端验证
强制: 始终调用Siteverify API验证令牌。
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>()
// 额外验证
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
}
// 在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验证失败', { status: 401 })
}
关键规则
始终做
✅ 调用Siteverify API - 服务器端验证是强制的
✅ 使用HTTPS - 切勿通过HTTP验证
✅ 保护秘密密钥 - 切勿在前端代码中暴露
✅ 处理令牌过期 - 令牌5分钟后过期
✅ 实现错误回调 - 优雅处理失败
✅ 使用测试密钥测试 - 测试站点密钥: 1x00000000000000000000AA
✅ 设置合理超时 - 不要无限等待验证
✅ 验证操作/主机名 - 检查指定时的额外字段
✅ 定期轮换密钥 - 使用仪表板或API轮换秘密
✅ 监控分析 - 跟踪解决率和失败
✅ 在表单提交后验证令牌 - 在用户完成表单后验证令牌,而不是之前。过早验证会创建安全漏洞,攻击者可获取有效令牌然后绕过防护
切勿做
❌ 跳过服务器验证 - 仅客户端 = 安全漏洞 ❌ 代理api.js脚本 - 必须从Cloudflare CDN加载 ❌ 重用令牌 - 每个令牌仅单次使用 ❌ 使用GET请求 - Siteverify仅接受POST ❌ 暴露秘密密钥 - 仅在后端环境中保存秘密 ❌ 信任客户端验证 - 令牌可被伪造 ❌ 缓存api.js - 未来更新会破坏集成 ❌ 在测试中使用生产密钥 - 使用测试密钥代替 ❌ 忽略错误回调 - 始终处理失败
已知问题预防
此技能预防 12 个记录问题:
问题 #1: 缺少服务器端验证
错误: Turnstile分析仪表板中零令牌验证 来源: https://developers.cloudflare.com/turnstile/get-started/ 原因: 开发者仅实现客户端小部件,跳过Siteverify调用 预防: 所有模板包括强制服务器端验证与Siteverify API
问题 #2: 令牌过期 (5 分钟)
错误: 有效令牌延迟提交后 success: false
来源: https://developers.cloudflare.com/turnstile/get-started/server-side-validation
原因: 令牌生成300秒后过期
预防: 模板记录TTL并在过期时实现令牌刷新
问题 #3: 秘密密钥在前端暴露
错误: 安全绕过 - 攻击者可验证自己的令牌 来源: https://developers.cloudflare.com/turnstile/get-started/server-side-validation 原因: 秘密密钥硬编码在JavaScript或源中可见 预防: 所有模板显示仅后端验证与环境变量
问题 #4: GET请求到Siteverify
错误: API返回405方法不允许 来源: https://developers.cloudflare.com/turnstile/migration/recaptcha 原因: reCAPTCHA支持GET,Turnstile要求POST 预防: 模板使用POST与FormData或JSON正文
问题 #5: 内容安全策略阻塞
错误: 错误200500 - “加载错误: iframe无法加载” 来源: https://developers.cloudflare.com/turnstile/troubleshooting/client-side-errors/error-codes 原因: CSP阻塞challenges.cloudflare.com iframe 预防: 技能包括CSP配置参考和check-csp.sh脚本
问题 #6: 小部件崩溃 (错误300030)
错误: 合法用户的一般客户端执行错误 来源: https://community.cloudflare.com/t/turnstile-is-frequently-generating-300x-errors/700903 原因: 未知 - 似乎是Cloudflare端问题(2025) 预防: 模板实现错误回调、重试逻辑和后备处理
问题 #7: 配置错误 (错误600010)
错误: 小部件失败“配置错误” 来源: https://community.cloudflare.com/t/repeated-cloudflare-turnstile-error-600010/644578 原因: 小部件配置中缺少或删除主机名 预防: 模板记录主机名允许列表要求和验证步骤
问题 #8: Safari 18 / macOS 15 “隐藏IP”问题
错误: 错误300010当Safari的“隐藏IP地址”启用时 来源: https://community.cloudflare.com/t/turnstile-is-frequently-generating-300x-errors/700903 原因: 隐私设置干扰挑战信号 预防: 错误处理参考文档Safari解决方法(禁用隐藏IP)
问题 #9: Brave浏览器彩带动画失败
错误: 验证在成功动画期间失败 来源: https://github.com/brave/brave-browser/issues/45608 (2025年4月) 原因: Brave防护屏蔽动画脚本 预防: 模板在动画完成前处理成功
问题 #10: Next.js + Jest不兼容
错误: @marsidev/react-turnstile破坏Jest测试 来源: https://github.com/marsidev/react-turnstile/issues/112 (2025年10月) 原因: 与Jest的模块解析问题 预防: 测试指南包括Jest模拟模式和测试站点密钥使用
问题 #11: localhost不在允许列表
错误: 错误110200 - “未知域名: 域名不允许” 来源: https://developers.cloudflare.com/turnstile/troubleshooting/client-side-errors/error-codes 原因: 生产小部件在开发中使用未加localhost到允许列表 预防: 模板使用测试密钥用于开发,记录localhost允许列表要求
问题 #12: 令牌重用尝试
错误: success: false 带“令牌已使用”错误
来源: https://developers.cloudflare.com/turnstile/troubleshooting/testing
原因: 每个令牌仅能验证一次
预防: 模板记录单次使用约束和令牌刷新模式
配置
Wrangler (Workers): 加载 templates/wrangler-turnstile-config.jsonc 用于完整配置。关键设置: vars 用于公共站点密钥(安全提交),secrets 用于私有秘密密钥(使用 wrangler secret put TURNSTILE_SECRET_KEY)。
CSP指令 (如果使用内容安全策略):
<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;">
常见模式
Hono + Cloudflare Workers: Workers API路由中的服务器端验证与Hono框架。加载 references/common-patterns.md #pattern-1 当构建需要机器人防护的Workers端点时。
React + Next.js: 客户端表单与@marsidev/react-turnstile集成。加载 references/common-patterns.md #pattern-2 当集成Turnstile与React/Next.js应用程序时。
E2E测试: 自动测试与测试密钥(Playwright, Cypress, Jest)。加载 references/common-patterns.md #pattern-3 当编写E2E测试或设置CI/CD管道时。
小部件生命周期: SPA的编程小部件控制(渲染, 重置, 移除, 获取令牌)。加载 references/common-patterns.md #pattern-4 当构建需要显式小部件管理的SPA时。
何时加载参考
references/widget-configs.md: 配置小部件外观、主题、执行模式、大小、语言或重试行为。
references/error-codes.md: 调试错误代码100*, 200*, 300*, 400*, 600* 或故障排除客户端失败(CSP, 域名错误, 小部件崩溃)。
references/testing-guide.md: 设置E2E测试(Playwright, Cypress)、本地开发与测试密钥或CI/CD管道集成。
references/react-integration.md: 与React、Next.js集成或故障排除@marsidev/react-turnstile问题(Jest模拟, SSR, 钩子)。
references/common-patterns.md: 构建Hono Workers路由、React表单、E2E测试或小部件生命周期管理(显式渲染)。
references/advanced-topics.md: 实现SPA预清除、自定义操作/cdata、重试策略或多小部件页面。
references/setup-checklist.md: 准备部署、验证完整设置或确保生产就绪(14点检查列表)。
references/migration-guide.md: 从reCAPTCHA (v2) 或 hCaptcha迁移到Turnstile,包括兼容模式、API差异和仅POST Siteverify要求。
references/browser-support.md: 浏览器兼容性矩阵、Safari 18“隐藏IP”解决方法、Brave防护问题和浏览器特定后备。
references/mobile-implementation.md: WebView集成用于iOS、Android、React Native和Flutter,包括用户代理一致性和存储持久性要求。
templates/: wrangler-turnstile-config.jsonc (Workers环境),turnstile-widget-implicit.html (静态表单),turnstile-widget-explicit.ts (SPA渲染),turnstile-server-validation.ts (Siteverify API),turnstile-react-component.tsx (React集成),turnstile-hono-route.ts (Hono验证),turnstile-test-config.ts (测试设置)
scripts/check-csp.sh: 验证内容安全策略允许Turnstile(用法: ./scripts/check-csp.sh https://example.com)
依赖项
必需: 无(Turnstile从Cloudflare CDN加载)
可选: @marsidev/react-turnstile@1.3.1 (React), turnstile-types@1.2.3 (TypeScript), vue-turnstile (Vue 3), ngx-turnstile (Angular), svelte-turnstile (Svelte), @nuxtjs/turnstile (Nuxt)
官方文档
Turnstile: https://developers.cloudflare.com/turnstile/ • 快速开始: https://developers.cloudflare.com/turnstile/get-started/ • 错误代码: https://developers.cloudflare.com/turnstile/troubleshooting/client-side-errors/error-codes/ • 测试: https://developers.cloudflare.com/turnstile/troubleshooting/testing/ • 迁移(reCAPTCHA): https://developers.cloudflare.com/turnstile/migration/recaptcha/ • MCP: 使用 mcp__cloudflare-docs__search_cloudflare_documentation 工具
故障排除
问题: 错误110200 - “未知域名”
解决方案: 在Cloudflare仪表板中将您的域名(包括localhost用于开发)添加到小部件的允许域名。对于本地开发,使用测试站点密钥 1x00000000000000000000AA 代替。
问题: 错误300030 - 小部件对合法用户崩溃
解决方案: 实现错误回调带重试逻辑。这是已知的Cloudflare端问题(2025)。如果重试失败,后备到替代验证。
问题: 令牌总是返回 success: false
解决方案:
- 检查令牌是否未过期(5分钟TTL)
- 验证秘密密钥正确
- 确保令牌之前未验证过(单次使用)
- 检查主机名匹配小部件配置
问题: CSP阻塞iframe (错误200500)
解决方案: 添加CSP指令:
<meta http-equiv="Content-Security-Policy" content="
frame-src https://challenges.cloudflare.com;
script-src https://challenges.cloudflare.com;
">
问题: Safari 18 “隐藏IP”导致错误300010
解决方案: 在错误消息中记录用户应禁用Safari的“隐藏IP地址”设置(Safari → 设置 → 隐私 → 隐藏IP地址 → 关闭)
问题: Next.js + Jest测试失败与@marsidev/react-turnstile
解决方案: 在Jest设置中模拟Turnstile组件:
// jest.setup.ts
jest.mock('@marsidev/react-turnstile', () => ({
Turnstile: () => <div data-testid="turnstile-mock" />,
}))
令牌效率: ~65-70% 节省 vs 手动集成
预防错误: 12个记录的安全/验证问题带完整解决方案
部署检查列表: 加载 references/setup-checklist.md 用于完整14点预部署验证