Cloudflare Turnstile 网站表单与登录防机器人清单:sitekey、服务端验证和 hostname 限制怎么配

Turnstile 不需要把网站代理到 Cloudflare,也能在表单、登录和提交接口上挡住批量机器人,关键是服务端必须校验 token。

Turnstile 不需要把网站代理到 Cloudflare,也能在表单、登录和提交接口上挡住批量机器人,关键是服务端必须校验 token。

  1. 01先读摘要,判断是否与你的场景相关。
  2. 02再看来源,保留继续查证的路径。
  3. 03最后看步骤、风险和可复用动作。

很多站点已经在登录页和联系表单上用过 CAPTCHA,但因为用户体验差、脚本误拦或验证逻辑只写在浏览器端,机器人还是能绕过去。Cloudflare Turnstile 的差别在于:它可以不通过 Cloudflare CDN,在任何网站、静态站或自建后端中使用,而且官方把服务端 Siteverify 验证列为必须步骤。下面这套清单按从创建 Widget 到上线验收的顺序写。

适用场景

  • 你要保护登录表单、注册接口、联系页、评论提交或内容提交接口。
  • 站点不在 Cloudflare 代理后面,仍希望使用独立的人机验证服务。
  • 你正在从 reCAPTCHA 或 hCaptcha 迁移,想减少对用户交互的依赖。
  • 你有后端代码或服务端函数可以调用 HTTPS API 验证 token。

不适用场景

  • 表单提交根本没有后端,token 无法验证,这时加前端控件也只是装饰。
  • 你已经用 WAF 规则拦截全部非浏览器流量,但还没有区分真实用户和正常自动化。
  • 你要保护原生 App 内的 WebView,却没有按官方移动端说明处理。
  • 团队不能维护 secret key,也不想建立密钥轮换和 hostname 限制流程。

准备材料

  • 一个 Cloudflare 账号,以及能创建 Turnstile Widget 的权限。
  • 要保护的表单页面地址、提交接口和后端语言。
  • 生产、测试、开发三个环境对应的域名或 hostname 清单。
  • 后端能发起 HTTPS POST 请求的代码,以及日志记录能力。
  • 一个能模拟普通用户、异常浏览器和过期 token 的测试方法。

步骤一:为每个环境创建独立 Widget

官方文档建议使用描述性名称,并为开发、staging、生产创建不同 Widget。同一个 secret 跨环境复用,会让测试流量和生产流量混在一起,也增加密钥泄漏后的影响面。

  1. 进入 Cloudflare Dashboard 的 Turnstile 页面,选择 Add site / widget。
  2. 为生产表单命名为 “Login Form Production”,为测试表单命名为 “Login Form Test”。
  3. 填写允许的 hostname,例如 www.example.com 和 example.com,不要写入 localhost 以外的无关域名。
  4. 记录每个 Widget 的 sitekey 和 secret key,把 secret 放到后端环境变量,不要写进前端代码。

步骤二:客户端嵌入 Widget 并取得 token

Turnstile 支持 Managed、Non-interactive 和 Invisible 三种 widget 类型。Managed 会根据风险自动决定是否显示交互挑战,适合大多数表单。客户端只需加载脚本、放置容器,并把生成的 token 随表单一起提交。

<div class="cf-turnstile" data-sitekey="YOUR_SITEKEY"></div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
  • 不要把 sitekey 当成 secret,它本来就会出现在页面源码中。
  • 确保 token 字段随表单 POST 到后端,常用字段名是 cf-turnstile-response。
  • SPA 或异步提交时,在提交前读取当前 token,避免使用上一次挑战的旧值。

步骤三:服务端调用 Siteverify 验证 token

官方强调,不调用 Siteverify 会导致严重漏洞。token 最长 2048 字符,生成后 300 秒有效,且只能验证一次。后端必须把 secret、response 和可选的 remoteip 发送到 https://challenges.cloudflare.com/turnstile/v0/siteverify,然后根据 success 字段放行或拒绝。

POST https://challenges.cloudflare.com/turnstile/v0/siteverify
{
  "secret": "YOUR_SECRET_KEY",
  "response": "TOKEN_FROM_FORM",
  "remoteip": "OPTIONAL_IP"
}

验证失败时,官方返回 error-codes,例如 timeout-or-duplicate 表示 token 过期或已被使用。不要只检查 HTTP 状态,还要检查响应体里的 success 和 hostname 字段。即使 success 为 true,也应该确认 hostname 是否属于你允许的域名,防止 token 被别的站点借走。

步骤四:把验证失败接入业务日志和监控

Turnstile 只是防线中的一环。后端要在失败时返回可理解的错误,并记录 token 状态、来源 IP、接口路径和用户 agent,但不能把完整 token 或 secret 写入日志。

  • 对登录接口,验证失败不要继续尝试账号密码匹配。
  • 对表单接口,失败时直接拒绝,不要写入数据库。
  • 对正常用户,提示“请重新验证”或刷新页面,而不是显示技术错误。
  • 在 Cloudflare Turnstile Analytics 中查看 token validation 指标,确认没有大量 0 验证记录。

步骤五:上线前做五组测试

只测试正常浏览器不够。至少要覆盖以下路径:

  • 正常浏览器加载页面并提交,确认 token 验证成功。
  • 不带 token 直接 POST,确认后端拒绝。
  • 重复使用同一个 token,确认第二次返回 timeout-or-duplicate。
  • 等待超过 5 分钟后提交,确认提示过期并要求刷新。
  • 从非允许 hostname 伪造请求,确认即使 success 为 true 也会被 hostname 检查拦下。

可复制部署检查表

Widget 名称:
环境:
sitekey 是否仅前端:
secret 是否仅后端:
允许 hostname:
页面嵌入方式:
token 字段名:
Siteverify 端点:
失败返回码:
日志字段:
密钥轮换周期:
负责人:

实际例子:WordPress 登录页和联系表单加固

一个小型内容站没有把站点代理到 Cloudflare,但联系表单每天收到几十条垃圾提交。团队创建了 Production Contact 和 Production Login 两个 Widget,用插件或自定义主题代码把 Turnstile 放入表单,并在 wp-config 或服务器环境变量中保存 secret。后端钩子在 wp_ajax 和 wp_login 前调用 Siteverify,5 分钟过期和重复 token 都返回失败。两周后,垃圾提交明显减少,真实用户没有看到交互式验证码。

验收清单

  • 每个环境有独立 Widget,hostname 已限制。
  • sitekey 只在客户端出现,secret 只在服务端环境变量中出现。
  • 后端调用 Siteverify,且不只检查 HTTP 200。
  • 重复 token、过期 token、无 token 都已被测试。
  • Turnstile Analytics 中 token validation 指标不是 0。
  • 失败路径没有破坏正常表单提交。

常见坑

  • 只在浏览器端判断 token 存在,不调用 Siteverify。
  • 把 secret key 写在 HTML 或前端 JS 中。
  • 多个环境共用同一个 Widget,导致 hostname 和日志混乱。
  • 忽略 hostname 字段,让其他站点生成的 token 也能通过。
  • 不处理 5 分钟过期,用户填完长表单后提交失败。

排错路径

  • Siteverify 返回 missing-input-secret:检查后端是否传了正确的 secret 环境变量。
  • 返回 invalid-input-response:检查 token 是否完整,以及是否已经用过。
  • 返回 timeout-or-duplicate:提示用户刷新验证,不要反复重试同一个 token。
  • 页面无 Widget:检查脚本是否被 CSP 或广告拦截器阻止,并确认容器存在。
  • 成功但来源可疑:核对响应中的 hostname 是否在允许列表内。

后续维护建议

更新日期:2026-08-09。Turnstile 上线后建议每季度做一次密钥轮换、hostname 复查和测试用例回归。若站点新增域名、子域名或第三方提交入口,先更新 Widget hostname 再开放流量。不要把 Turnstile 当作唯一安全层,服务端权限、速率限制和异常日志仍然要保留。

公开来源

  1. Cloudflare Docs: Turnstile Overview
  2. Cloudflare Docs: Get Started
  3. Cloudflare Docs: Validate the Token

订阅更新

输入邮箱,订阅站点更新。

参与讨论

你的邮箱不会公开。 标有 * 的为必填项。