turnstile-spin

turnstile-spin

热门

在项目中端到端配置 Cloudflare Turnstile。扫描代码库,通过 Cloudflare API 创建 Widget,将其嵌入到相应的表单中,在客户现有的后端服务中接入标准的服务端 siteverify 验证逻辑,校验无误后持久化保存该 Skill。当用户提出添加 Turnstile、配置验证码(CAPTCHA)、保护表单防 Bot 攻击或修复 Turnstile 集成时加载此 Skill。对标 developers.cloudflare.com/turnstile/spin。

2146Star
202Fork
更新于 2026/7/13
SKILL.md
只读
名称
turnstile-spin
描述

在项目中端到端配置 Cloudflare Turnstile。扫描代码库,通过 Cloudflare API 创建 Widget,将其嵌入到相应的表单中,在客户现有的后端服务中接入标准的服务端 siteverify 验证逻辑,校验无误后持久化保存该 Skill。当用户提出添加 Turnstile、配置验证码(CAPTCHA)、保护表单防 Bot 攻击或修复 Turnstile 集成时加载此 Skill。对标 developers.cloudflare.com/turnstile/spin。

Turnstile Spin Skill

把“配置 Turnstile”这一 Prompt 转化为真正可用的端到端集成方案:创建 Widget、在所有选定的插入点嵌入前端代码片段、在客户现有的后端接入标准的服务端 siteverify,并在最终汇报成功前完成真实的校验测试。

你就是 Agent。请通过调用 scripts/ 目录下的脚本并根据其 JSON 输出进行分支处理,来运行以下向导。脚本负责确定性的逻辑(API 调用、重试/错误处理);你的职责是流程编排、读取代码库、确认交互以及进行前端与后端的代码修改。

标准官方指南位于 developers.cloudflare.com/turnstile/spin。若文档页面与本文件内容有冲突,请以官方文档页面为准。

何时加载此 Skill

当用户的 Prompt 中包含以下任意内容时加载:

  • “Turnstile”、“CAPTCHA”、“人机验证”、“Bot 防护”
  • “siteverify”、“cf-turnstile-response”
  • “保护这个表单”、“防 Bot 注册”、“防垃圾注册”
  • 具体的注册、登录或联系表单,并结合了“Cloudflare”或“Bot”等关键词

除非同时提到了 Turnstile,否则不要在处理无关的 Cloudflare 任务(如 Workers、Pages、R2 等)时加载此 Skill。

对话流程

用户发送了 Prompt。你正处于多步骤对话中。尽最大可能自动检测,仅在必要时提问,且在执行每个不可逆步骤前必须先进行确认。每一个带编号的节点代表 Agent 发送的一条消息。标记有 [等待用户回应] 的项目需要用户给予答复。

  1. 简要确认。 用一句话回复:“我将为你端到端运行 Turnstile 配置流程。包含:检查鉴权、扫描代码库、创建 Widget、嵌入对应表单、接入服务端 siteverify、执行校验。是否继续?”[等待用户回应] 此时先不要展示具体计划,鉴权 + 扫描优先。

  2. CLI 检查。 Spin 的辅助脚本会使用 curl 请求 api.cloudflare.com,并使用 npx wrangler whoami 进行账号枚举。第 8 步创建 Widget 时,若子命令可用(Wrangler 4.109+),优先使用 wrangler turnstile widget create;否则降级使用内置的 curl 脚本。无需持久化安装 CLI。

  3. 鉴权 + 权限探针(第一个不可逆操作)。 运行 scripts/auth-probe.sh。根据返回的 status 进行分支处理:

    • ok:继续执行第 4 步。脚本已自动选择账号(单账号 Token,或与 $CLOUDFLARE_ACCOUNT_ID 匹配的账号)。
    • missing_tokenmissing_scope:提示用户在 https://dash.cloudflare.com/profile/api-tokens 页面创建 Token → 选择 Custom token → 权限设置为 Account.Turnstile:Edit → 在 Account Resources 中包含目标账号。不要引导用户去执行 wrangler login,除非 wrangler 的 OAuth scope 中包含了 Account.Turnstile:Edit(不同 wrangler 版本有所差异)。按从优到劣提供三种提交 Token 的方式:
      1. 环境变量导出 + 重新启动(Token 绝不进入聊天记录):export CLOUDFLARE_API_TOKEN=<token>,然后在该 Terminal 中重启 Agent。
      2. 保存到本地文件(Token 保存在仅当前用户有权访问的文件中,不进入聊天记录):umask 077 && printf '%s' '<token>' > ~/.cf-turnstile-token,随后通过 TOKEN=$(cat ~/.cf-turnstile-token) 读取。
      3. 直接在聊天框粘贴(速度最快,但 Token 会存入对话日志;如果日志后续会被共享,用户应在事后轮换 Token)。
        若用户选择方案 3(直接粘贴),你可以利用等待时间预先执行第 5、6、7 步(域名扫描、代码库扫描、插入计划)。方案 1 和 2 会重启 Session,因此在这两种情况下不要预先获取状态。鉴权建立完成后,重新运行 auth-probe.sh,再继续执行第 8 步。
    • multiple_accounts:Token 包含多个账号,且未设置 $CLOUDFLARE_ACCOUNT_ID。展示带编号的 accounts 账号列表。[等待用户回应] 随后导出 CLOUDFLARE_ACCOUNT_ID=<所选账号> 并重新运行 auth-probe.sh
    • account_mismatch:已设置 $CLOUDFLARE_ACCOUNT_ID 但不属于该 Token 覆盖的账号。展示 accounts 列表,请用户 unset CLOUDFLARE_ACCOUNT_ID 或将其设置为列表中的有效 ID。
  4. 账号选择。auth-probe.sh 在经历 multiple_accounts 往返后返回 ok,则此步骤已完成。否则脚本已静默选择了唯一的账号,直接进入第 5 步。

  5. 域名配置。 始终包含 localhost127.0.0.1。对于生产环境域名,扫描 package.json 中的 homepagewrangler.tomlREADME.mdAGENTS.md 以及 git remote。确认:“我将为 localhost127.0.0.1 以及 <domain> 注册 Widget。是否确认?”[等待用户回应] 如果未检索到生产环境域名,请向用户询问。

  6. 代码库扫描。 静默检测三项内容:

    • 前端框架(Next.js、Astro、SvelteKit、Hugo、原生 HTML 等)→ 决定 Widget 嵌入代码片段。
    • 后端 Handler 位置(Express 路由、Next.js API 路由、Rails Controller、Workers fetch handler、Pages Function 等)→ 决定 siteverify 代码片段。
    • 已有的 CAPTCHA(reCAPTCHA / hCaptcha)→ 将第 7 步切换为迁移模式。
  7. 插入计划。 展示带有 [recommended](推荐)/ [skip by default](默认跳过)标记的候选表单列表;请用户确认(回复序号、“全部”、“推荐”或指定列表)。[等待用户回应] 若检测到已有的 CAPTCHA,则展示迁移计划(参见“从其他 CAPTCHA 迁移”)。

  8. Widget 创建。 当 wrangler CLI 的 turnstile widget 子命令可用时优先使用:

    npx wrangler turnstile widget create "<name>" \
      --domain <d1> --domain <d2> ... --mode managed --json
    

    从 stdout 的 JSON 中解析出 sitekeysecret。若 wrangler 不存在、版本低于 turnstile 子命令(提示 unknown command)或执行失败,降级使用 scripts/widget-create.sh --account-id <id> --name <name> --domains <list> --mode managed,该脚本将直接通过 curl 调用 Cloudflare API。告知用户 sitekey。将 secret 捕获到 Shell 变量 WIDGET_SECRET 中;切勿将其写入磁盘,除非在第 9 步将其写入用户自己的环境变量 / Secret 存储中。

  9. 接入集成代码。 明确代码变更约定:“我将在每个选定的表单中嵌入 Widget,并在你现有的提交 Handler 中插入标准的 siteverify 调用,以 success === true 作为关卡校验。Handler 现有的核心逻辑保持不变。Secret 将以 TURNSTILE_SECRET 的形式保存在环境变量中。”询问“确认”还是“查看 Diff”。[等待用户回应] 若用户选择“查看”,打印 Unified Diff 并再次确认。切勿自行提出其他额外的行为变动(如邮件发送、自定义后端等)。

    标准的服务端 siteverify(Node / fetch 惯用法;根据检测到的后端进行适配):

    const r = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        secret: process.env.TURNSTILE_SECRET,
        response: token,         // 来自请求的 cf-turnstile-response
        remoteip: clientIp,      // X-Forwarded-For / req.ip 等
      }),
    });
    const result = await r.json();
    if (!result.success) {
      return reject(403, 'forbidden');  // 平台对应的拒绝逻辑
    }
    // 现有 Handler 逻辑在此处继续运行,保持不变
    

    将 Secret 写入用户的密钥存储中(Node/Rails/Python 使用 .env,Workers 使用 wrangler secret put TURNSTILE_SECRET,Vercel / Fly / Render 等使用对应平台的 Secret 管理器)。切勿硬编码在代码中。

  10. 校验测试。 运行 scripts/validate.sh。逐项汇报通过的检查项。若有任何检查失败,暴露错误并停止后续流程。[若有失败,等待用户回应]

  11. 持久化保存 Skill。 询问:“是否将 Spin Skill 保存至 .claude/skills/turnstile-spin/SKILL.md,以便在后续任务中复用?”默认确认。[等待用户回应] 随后运行 scripts/persist-skill.sh --path <agent-specific-path>

  12. 最终汇报。 输出结构化的总结报告:创建了什么、校验了什么、下一步建议。

严禁执行事项

  • 切勿将 Turnstile Secret 写入磁盘,除非是作为用户自身环境变量 / Secret 存储的一部分。
  • 切勿跳过校验步骤。
  • 未展示 Diff 前切勿直接覆盖文件。
  • 切勿在浏览器前端直接调用 siteverify。永远遵循:浏览器 → 用户后端 → siteverify。
  • 切勿部署任何额外的基础设施(Workers、代理、Sidecar 等)。客户现有的后端直接调用 siteverify 即可。
  • 未经询问切勿使用 sudo 或全局安装 npm 包。
  • 未经要求切勿在向导范围外提议其他功能(如自定义 Workers、自定义域名、高级 WAF 规则等)。

严格范围边界:切勿向用户询问

Spin 的作用是在用户现有的表单 Handler 执行前,通过标准 siteverify 校验 Turnstile Token。其他所有事项均超出本 Skill 范围:

  • 邮件 / 短信 / 消息通知发送。 保持现有提交 Handler 原封不动(仅增加 success === true 校验门禁)。不要提议 Resend、Mailchannels、SMTP 或 mailto。
  • 添加新的后端服务。 如果表单目前没有后端 Handler(纯静态站点、仅 mailto 的联系表单),直接说明情况并退出。Spin 需要一个服务端位置来放置 siteverify。
  • 数据库 / 支付 / OAuth / 表单持久化。 超出范围。
  • 前端框架迁移、重构或样式修改。 仅修改必要的代码。
  • reCAPTCHA v3 分值阈值。 Turnstile 仅返回 success: true/false
  • 仅 Pre-clearance(预通关)模式配置。clearance_level !== no_clearance,则 siteverify 是可选的,Spin 并不适用。请引导用户并退出。

恢复流程:尊重现有的 Widget 配置

当用户拥有 Cloudflare Dashboard 访问权限时,Dashboard 内的 Fix with Spin 横幅是一个一键恢复入口:它会展示针对已有 Widget 调优后的 Agent Prompt。下方本 Skill 的恢复流程则是用户在编辑器环境中操作时的对应等价流程。

如果用户告知你他们已经配置好了 Turnstile Widget,希望在不轮换 sitekey 的前提下接入 siteverify(例如“我有 sitekey 但 siteverify 一直没调通”、“基于我已有的 Widget <sitekey> 配置 Spin”):

  1. 跳过第 8 步(创建 Widget)。sitekey 已经存在,直接从用户处获取。
  2. 通过 scripts/fetch-secret.sh --account-id <id> --sitekey <key> 获取 Widget 元数据。根据 status 分支处理:
    • ok:从响应中读取 secretclearance_leveldomains。确认 domains 包含用户的生产环境主机名;如果不包含,在继续前先指出缺失之处。
    • missing_read_scope:提示用户向 Token 添加 Account.Turnstile:Read 权限,或降级为请用户手动粘贴 Secret。在手动粘贴路径下,你将无法获取 clearance_leveldomains,需向用户二次确认这两项。
  3. 检查响应(或用户回答)中的 clearance_level
    • no_clearance:标准接入流程(第 9 步)。
    • 其他任何值:询问用户是否想在 Pre-clearance 之上再加一层 siteverify,或者按照范围边界规则退出。
  4. 从第 9 步(接入集成代码)继续。Site key 保持不变,已有 Widget 在整个过程中持续正常工作。
  5. 切勿为了获取新的 Secret 而重新创建 Widget,这会导致所有已部署该 sitekey 的地方全部失效。

前端修改约定

接入已有表单时(第 9 步),代码修改约定为:加门禁,不替换。 用户现有的提交 Handler 保持原有的所有行为,Spin 仅在其之前增加一层校验步骤。

前端(嵌入 Widget 并提交至用户现有的 Endpoint):

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

<form action="/signup" method="POST">
  <!-- 现有 input 输入框保持不变 -->
  <div class="cf-turnstile" data-sitekey="<SITEKEY>" data-action="turnstile-spin-v2"></div>
  <button type="submit">Sign up<