人机验证 Turnstile
CWD 评论系统支持接入 Cloudflare Turnstile,在提交评论和回复前要求访客完成一次人机验证,用来阻挡批量刷评的机器人。Turnstile 由 Cloudflare 免费提供,Managed 模式下大多数访客无需任何操作即可通过,也不能被用来追踪访客。
开启过程分两步:先在 Cloudflare 控制台申请一对密钥,再把密钥配置到 Worker 环境变量。密钥不属于后台设置项,不需要写进数据库。
申请密钥
前提条件
- 一个 Cloudflare 账号(使用邮箱即可注册,官网地址)
- 评论页面所在的域名
域名不要求托管在 Cloudflare 上,Turnstile 可以独立使用,也不要求站点流量经过 Cloudflare。
创建 Widget
- 登录 Cloudflare 控制台,在左侧菜单选择 Turnstile,也可以直接访问
https://dash.cloudflare.com/?to=/:account/turnstile - 点击 Add widget
- 填写表单:
- Widget name:自定义名称,例如
CWD Comments - Hostname management:评论页面所在的域名,例如
blog.example.com,可以添加多个 - Widget mode:选择 Managed(推荐)
- Widget name:自定义名称,例如
- 点击 Create
- 页面会显示 Sitekey 和 Secret Key,Secret Key 只在创建时完整显示,请立即妥善保存
Widget mode 的可选项:
| 模式 | 说明 |
|---|---|
| Managed | 根据访客风险自动选择无感验证或勾选框验证,推荐使用 |
| Non-Interactive | 只显示加载状态,不要求访客点击 |
| Invisible | 完全在后台运行,需要同时在隐私政策中引用 Cloudflare 的相关条款 |
免费额度
免费计划足够个人站点和大多数生产环境使用,无需付费升级:
| 项目 | 免费计划 |
|---|---|
| Widget 数量 | 每个账号最多 20 个 |
| Hostname 数量 | 每个 Widget 最多 10 个 |
| 验证次数 | 不限 |
| 数据分析 | 保留 7 天 |
配置到评论系统
拿到密钥后,在 Worker 中配置以下环境变量:
| 变量名 | 对应控制台字段 | 配置方式 |
|---|---|---|
TURNSTILE_SITE_KEY | Sitekey | 普通变量,写在 wrangler.jsonc 的 vars |
TURNSTILE_SECRET_KEY | Secret Key | Wrangler Secret 或控制台加密变量 |
TURNSTILE_ALLOWED_HOSTNAMES | Hostname management | 可选,普通变量,多个域名以逗号分隔 |
私密密钥建议使用 Wrangler Secret 保存,不会出现在配置文件中:
cd cwd-api
npx wrangler secret put TURNSTILE_SECRET_KEY站点密钥和允许域名可以写在 wrangler.jsonc 的 vars 中:
{
"vars": {
"TURNSTILE_SITE_KEY": "0x4AAAAAAA...",
"TURNSTILE_ALLOWED_HOSTNAMES": "blog.example.com,www.example.com"
}
}修改 wrangler.jsonc 后需要重新执行 npx wrangler deploy 才会生效,wrangler secret put 则会立即生效。
前端无需额外配置
服务端同时配置站点密钥和私密密钥后,评论组件会从 /api/config/comments 自动读取 turnstileSiteKey,并在提交新评论和回复前渲染验证码。只有在需要覆盖后端配置时,才需要在前端实例化时传入 turnstileSiteKey,参见前端配置。
本地调试
Cloudflare 提供了一组官方测试密钥,可以在本地和测试环境使用,不消耗额度也不会影响真实访客:
| Sitekey | Secret Key | 效果 |
|---|---|---|
1x00000000000000000000AA | 1x0000000000000000000000000000000AA | 永远验证通过 |
2x00000000000000000000AB | 2x0000000000000000000000000000000AA | 永远验证失败 |
测试密钥在任意域名(包括 localhost)都可以使用。测试 token 的 hostname 不是你的真实域名,因此使用测试密钥时不要配置 TURNSTILE_ALLOWED_HOSTNAMES,否则服务端会因为域名不匹配而拒绝评论。
本地开发可以写在 cwd-api/.dev.vars 中(该文件已在 .gitignore 中忽略,不会提交):
TURNSTILE_SITE_KEY=1x00000000000000000000AA
TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA校验规则
开启后,服务端会在写入评论前依次完成以下检查:
- 请求必须携带 Turnstile token,缺失或不合法时返回 403「请完成人机验证后再提交评论」
- 调用 Siteverify 接口校验 token,token 是一次性的,重复使用会被判为无效
- 校验结果中的
action必须为comment,该动作由评论组件固定传入,无需手动配置 - 配置了
TURNSTILE_ALLOWED_HOSTNAMES时,token 中的 hostname 必须与列表中的某一项完全一致
Siteverify 接口不可用时返回 503「人机验证服务暂时不可用,请稍后再试」。同一 IP 的冷却检查在校验之前执行,冷却期内的重复提交不会浪费一次性的 token。
常见问题
评论全部提示「请完成人机验证后再提交评论」
通常是只配置了 TURNSTILE_SECRET_KEY 而漏配 TURNSTILE_SITE_KEY。服务端检测到私密密钥后就会强制校验,而前端拿不到站点密钥、无法渲染验证码,于是所有提交都会被拒绝。两个密钥必须成对配置。
配置了允许域名后评论被拒绝
TURNSTILE_ALLOWED_HOSTNAMES 是精确匹配,不支持通配子域名。example.com、www.example.com、comments.example.com 需要分别列出,并且要与 Widget 的 Hostname management 保持一致。
如何关闭人机验证
删除 Worker 中的 TURNSTILE_SECRET_KEY 即可。服务端会跳过校验,前端也不再渲染验证码,无需改动前端代码。