CloudflareTurnstile机器人防护 cloudflare-turnstile

此技能提供Cloudflare Turnstile的集成指南,用于替代CAPTCHA实现网站机器人防护,防止垃圾邮件和恶意攻击。适用于前端框架如React、Next.js,后端如Cloudflare Workers、Hono,并包括服务器端验证和故障排除。关键词:Turnstile, CAPTCHA, 机器人防护, siteverify, 云安全, bot protection, 身份验证

身份认证 0 次安装 0 次浏览 更新于 3/8/2026

名称: 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: 创建小部件配置

  1. 登录Cloudflare仪表板
  2. 导航到Turnstile部分
  3. 点击“添加站点”
  4. 配置:
    • 小部件模式: 托管(推荐)、非交互或无形
    • 域名: 添加允许的主机名(如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

解决方案:

  1. 检查令牌是否未过期(5分钟TTL)
  2. 验证秘密密钥正确
  3. 确保令牌之前未验证过(单次使用)
  4. 检查主机名匹配小部件配置

问题: 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点预部署验证