验证码这件事,本来不该这么繁琐
网站刚加上登录框,撞库脚本就找上门。所谓「撞库」,就是机器人拿着泄露的账号密码,一遍遍尝试登录。注册页才上线,假账号也可能批量涌入;评论区一开放,很快就会塞满广告链接。
很多网站会用 reCAPTCHA 应对这类问题:先放一个「我不是机器人」的勾选框,遇到可疑用户,再让对方辨认红绿灯、斑马线。这套流程会打断操作,还依赖 Google,并会收集用户行为数据。超过免费额度后,reCAPTCHA 按调用量收费,流量越大,账单越高。
Turnstile 是 Cloudflare 推出的智能验证码,对用户几乎无感,对开发者免费、不限调用量,也更注重隐私。 你可以把它想成站在网站门口的保安:它先在后台观察这次访问是否正常,只有觉得可疑时,才会请访问者额外确认。大多数真人不需要认图、输字符,甚至感觉不到它的存在。
本文依据 2026 年 7 月的 Cloudflare Turnstile 官方文档[1] 编写。相关功能、参数和控制台界面可能继续调整,请以官方页面为准。
📌 一句话定位:Turnstile = 免费、无限量、无感、隐私友好的 reCAPTCHA 替代方案。
它到底怎么「看出来」你不是机器人?
reCAPTCHA 倾向于给访问者出题,Turnstile 则把大部分检查放在后台。整个过程可以理解为「前端领通行证,服务端验通行证」:
- 1. 用户打开页面后,浏览器会加载一小段 Turnstile 的 JavaScript 代码,并自动做一组后台检查。这里的「前端」,就是用户能打开和操作的网页。
- 2. Turnstile 会查看浏览器环境是否正常、是否带有自动化工具的特征,再交给 Cloudflare 的模型评估风险。
- 3. 风险较低时直接通过,浏览器会拿到一个一次性 token。token 可以理解为一张临时通行证。风险较高时,页面才会出现勾选框,让用户手动确认。
- 4. 用户提交表单时,网页会把 token 一起交给你的服务端。这里的「服务端」,就是接收登录、注册等请求的后台程序。服务端再调用
siteverify 接口,请 Cloudflare 检查这张通行证是真是假。
Token 只能使用一次,并会在短时间内过期。 这和一次性门票类似,用过就作废。即使攻击者截获了 token,也不能拿它反复提交请求。因此,每次提交表单时,服务端都要重新校验。对正常用户来说,页面通常只会短暂显示一个小验证框,甚至完全没有可见变化。
三种挂件类型:按场景挑一个
Turnstile 提供三种挂件类型。这里的「挂件(widget)」就是嵌在网页里的验证框,三种类型的主要区别是它是否可见、用户是否需要操作:
| | | |
|---|
| Managed(托管) | | | |
| Non-Interactive(非交互) | | | |
| Invisible(隐形) | | | 提交按钮、API 调用等不希望出现任何 UI 的场景 |
如果你不知道该选哪个,直接选 Managed。这是官方推荐的默认选项:普通用户通常无感通过,只有可疑访问才需要点一下,适合登录、注册和评论等大多数场景。
选好类型后,还可以设置挂件什么时候出现。可见挂件(Managed / Non-Interactive)支持三种 appearance(显示方式):
- •
execute:调用 turnstile.execute() 后才显示,适合配合表单提交时机。 - •
interaction-only:只有需要用户交互时才显示,大多数用户不会看到挂件。
⚠️ 如果你只是想先把 Turnstile 跑起来,保留默认的 always 即可。appearance 只影响可见挂件,Invisible 类型无论怎么设置都不会显示。
免费额度 & 定价
Turnstile 对所有 Cloudflare 套餐免费,而且不限调用量和域名数量。每验证一次,就算一次调用。Turnstile 不按调用次数计费,也不会在用量超过某个数字后突然开始收费。
| | |
|---|
| 无限 | |
| 不限 | |
| Free 即可 | 不需要 Pro / Business / Enterprise |
| | Managed / Non-Interactive / Invisible 都免费 |
| | |
reCAPTCHA 虽然提供免费额度,但企业版(reCAPTCHA Enterprise)按验证量收费,参考价格约为每千次 $1,具体以 Google 官方定价为准。网站流量越大,这笔开销就越高。它还依赖 Google,并会收集用户行为数据。
你的域名不需要托管在 Cloudflare,也不需要让网站流量经过 Cloudflare,照样可以接入 Turnstile。你只需要一个 Cloudflare 账户,并在网页和服务端各加入少量代码。
5 分钟快速开始
接入过程只有三步:先在 Cloudflare 创建挂件,再把挂件放进网页,最后让服务端校验验证结果。第三步最重要,不能省略。
第一步:在控制台创建挂件
- 1. 打开 Cloudflare 控制台,进入目标站点的 Turnstile。
- 2. 点击 Add widget(添加挂件),填写名称,并把站点域名加入
Domains。 - 3. 选一个
Mode(推荐 Managed),保存。 - • Site Key:相当于挂件的公开编号,放在网页里,被别人看到也没关系。
- • Secret Key:相当于后台密码,只能放在服务端环境变量中,绝不能写进网页或提交到代码仓库。
如果你只是给一个网站接入 Turnstile,可以跳过下面这段。需要管理很多网站时,才有必要用 API 批量创建挂件:
curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/challenges/widgets" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "My Login Widget",
"domains": ["example.com"],
"mode": "managed"
}'
第二步:前端嵌入挂件
现在把验证框放进网页。最简单的方法是「隐式渲染」,意思是只在 HTML 里留一个位置,官方脚本会自动把挂件画出来:
<form id="login-form" action="/api/login" method="POST">
<input type="text" name="username" placeholder="用户名" />
<input type="password" name="password" placeholder="密码" />
<div class="cf-turnstile"
data-sitekey="YOUR_SITE_KEY"
data-callback="onTurnstileSuccess"></div>
<button type="submit">登录</button>
</form>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" defer></script>
<script>
function onTurnstileSuccess(token) {
// token 已经生成,随表单一起提交即可
console.log("Turnstile token:", token);
}
</script>
上面的写法能满足普通 HTML 表单。如果你的表单由 JavaScript 提交,或者需要自己控制挂件何时出现,再使用下面的「显式渲染」。初次接入时可以先跳过:
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" defer></script>
<div id="turnstile-widget"></div>
<script>
window.onload = () => {
turnstile.render("#turnstile-widget", {
sitekey: "YOUR_SITE_KEY",
appearance: "always",
callback: (token) => console.log("通过:", token),
"error-callback": (code) => console.error("出错:", code),
"expired-callback": () => console.log("token 已过期"),
});
};
</script>
第三步:服务端校验 token(关键!)
网页显示验证通过,不代表这次请求一定可信。 攻击者可以不打开你的网页,直接向登录接口发送请求。服务端必须拿着 Secret Key 调用 siteverify,让 Cloudflare 确认 token 是真的、没过期、也没有被用过。
下面是 TypeScript 示例。它接收 token、Secret Key 和可选的用户 IP,验证成功返回 true,失败返回 false:
async function validateTurnstile(
token: string,
secret: string,
ip?: string,
): Promise<boolean> {
const res = await fetch(
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
secret,
response: token,
...(ip ? { remoteip: ip } : {}),
}),
},
);
const data = await res.json();
return data.success === true;
}
Cloudflare 不只会返回「成功」或「失败」,还会附带一些信息。刚开始接入时,先关注 success 和 error-codes 就够了:
| |
|---|
success | |
challenge_ts | |
hostname | |
action | |
cdata | |
error-codes | |
常见错误码包括:timeout-or-duplicate(token 已过期或使用过)、invalid-input-secret(Secret Key 错误)、invalid-input-response(token 无效)、invalid-input-remoteip(用户 IP 不匹配)。遇到问题时,可以先根据这些错误码定位原因。
实战场景
场景 1:给登录接口加一道人机校验
下面用 Cloudflare Worker 演示完整顺序。代码先检查 Turnstile,确认不是普通机器人后,再检查用户名和密码:
export async function onRequestPost(context: any) {
const { request, env } = context;
const { username, password, token } = await request.json();
// 1) 先校验 Turnstile
const ip = request.headers.get("CF-Connecting-IP") ?? undefined;
const ok = await validateTurnstile(token, env.TURNSTILE_SECRET_KEY, ip);
if (!ok) {
return new Response(JSON.stringify({ error: "人机验证失败" }), {
status: 403,
headers: { "Content-Type": "application/json" },
});
}
// 2) 再走你原来的登录校验
const valid = await checkCredentials(username, password);
if (!valid) {
return new Response(JSON.stringify({ error: "账号或密码错误" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
return new Response(JSON.stringify({ message: "登录成功" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
请把 Secret Key 存入 Worker 的环境变量(Wrangler vars / .dev.vars),不要直接写在源码里。否则代码一旦公开,别人就能拿走这把「后台钥匙」。
场景 2:与 WAF 和 Bot Management 联动
网站流量变大后,可以把 Turnstile 与 WAF、Bot Management 配合使用。它们像三道位置不同的门,各自拦截一类风险:
- • Bot Management / Bot Fight Mode:机器人管理功能,在请求到达网站程序前,先识别并挑战明显的自动化流量。
- • Turnstile:在关键表单(登录、注册、结账)前再补一道显式验证。
- • WAF 自定义规则:WAF 是 Web 应用防火墙,可以根据你设置的规则,直接拦截反复验证失败或绕过网页的请求。
常见做法是先用 Bot Score(机器人风险分数)过滤明显的垃圾流量,再在登录页用 Turnstile 检查剩下的可疑请求,最后由 WAF 拦截反复失败等异常行为。个人小站不必一开始就配齐三层,先正确接入 Turnstile 即可。
场景 3:Pre-Clearance(预验证),让老用户全程无感
如果网站有多个页面需要验证,可以让用户验证一次,后续页面无感通行。开启 Pre-Clearance(预验证)后,用户第一次通过挑战时,浏览器会拿到一个在当前会话中有效的凭证。之后访问站内其他受保护页面,就不必重复验证。
这很适合多步结账和分成几页填写的长表单:用户只在第一步接受验证,后续步骤不会再被打断。普通登录或评论表单暂时用不到,可以先跳过。
场景 4:A/B 测试,从 reCAPTCHA 平滑迁移
Cloudflare 提供 reCAPTCHA 兼容模式,方便你逐步迁移。比如先让 10% 的访问使用 Turnstile,其余访问继续使用 reCAPTCHA,这就是「灰度迁移」。对比两边的验证通过率、真人被误拦的比例和表单完成率后,再决定是否全部切换。兼容模式也能减少迁移期间的服务端改动。
成本计算器:为什么它「免费」最值钱
验证码通常按验证次数收费。假设你的网站每月要验证 100 万次表单提交:
| | |
|---|
| Cloudflare Turnstile | | $0 |
| reCAPTCHA Enterprise(参考价) | | |
| | |
以上数字只是估算,请以各厂商最新的官方定价为准。Turnstile 不随验证量增加而收费,无论验证 100 万次还是 1 亿次,直接费用都是 $0。
对独立开发者来说,这意味着不必在安全防护和验证码账单之间权衡。
最佳实践 & 避坑指南
- 1. 一定要在服务端校验 token。 网页里的
success 回调只负责更新界面,真正的安全检查由 siteverify 完成。漏掉这一步,攻击者就能绕过挂件,直接请求接口。 - 2. Secret Key 只能放在服务端。 用环境变量或 Secrets 管理,不要把它打包进网页代码,也不要提交到 Git。
- 3. 不要保存或重复使用 token。 token 只能使用一次,而且很快会过期。用户重新提交表单,或者触发
expired-callback 后,都要重新验证。 - 4. 需要更严格的校验时,再加入
remoteip 和 action。 你可以同时提交用户 IP,并检查返回的 action 是否与当前操作一致,比如登录请求只能接受 login。 - 5. 告诉用户验证出了什么问题。 为
error-callback、expired-callback、timeout-callback 显示「验证失败,请重试」或「验证已过期」等提示,不要让页面一直转圈。 - 6. 不同网站使用不同挂件。 按域名或业务创建独立 widget,方便分别查看数据。即使某个 Secret 泄露,影响也不会扩散到所有网站。
- 7. 需要分析数据时,再使用
action 和 cdata。 可以给请求加上 login、signup 等标签,然后在 Turnstile 面板中分别查看登录和注册的通过率。 - 8. 不要把 Turnstile 当作唯一防线。 它只能判断访问者是否可能是真人。真人也可能盗号或攻击网站,所以密码校验、权限检查和数据库安全措施仍然不能少。
- 9. 有欧盟数据合规要求时,设置数据区域。 创建 widget 时把
region 设为 eu,并在网页脚本中设置 data-region="eu";没有这类要求时,使用默认值 world 即可。 - 10. 上线后先观察,再调整。 先用 Managed 默认模式运行一段时间,查看正常用户有没有被误拦,再决定是否加强高风险页面的验证。
Turnstile vs reCAPTCHA:到底差在哪?
| | |
|---|
| 完全免费、无限量 | |
| | |
| | |
| | |
| 任意站点(不需走 Cloudflare 代理也能用挂件) | |
| | |
| | |
如果你更看重低成本、低打扰和隐私保护,Turnstile 是更合适的选择;是否继续使用 reCAPTCHA,则取决于站点对 Google 生态的依赖程度。
上线检查清单
- • [ ] 已在控制台创建挂件,拿到 Site Key 和 Secret Key
- • [ ] Site Key 放在网页中,Secret Key 只放在服务端环境变量中
- • [ ] 已选择挂件类型,不确定时使用 Managed
- • [ ] 服务端每次都调用
siteverify 校验 token - • [ ] 已处理
success=false;需要更严格校验时,再传入 remoteip - • [ ] 验证出错、过期或超时时,页面会告诉用户如何重试
- • [ ] 需要欧盟数据驻留时,已把挂件的
region 设为 eu - • [ ] 已在 Turnstile 面板观察通过率和真人被误拦的情况
- • [ ] 登录鉴权、权限检查和数据库安全仍由网站程序负责
最后的判断
Turnstile 把人机验证的直接成本降到 零:不限调用量、不限域名、所有套餐免费,同时很少打扰正常用户。对独立开发者来说,它尤其适合保护登录、注册和评论表单。
Turnstile 只能判断访问者是否可能是真人,不能判断这个人有没有访问权限。 用户通过验证后,网站仍要检查密码和权限,并安全地处理提交的数据。小型网站先做好 Turnstile 的服务端校验,就已经拦住了最常见的自动化请求;流量和风险提高后,再考虑加入 Bot Management 和 WAF。
相关资源
- • Cloudflare Turnstile 官方文档[1]
引用链接
[1] Cloudflare Turnstile 官方文档: https://developers.cloudflare.com/turnstile/
阅读原文:点击这里
该文章在 2026/7/18 0:49:41 编辑过