Cloudflare Turnstile 配置完全指南:脚本加载、组件选项与框架集成的实战手册(Codex Skills 仓库深度解析)
2026/9/13 2:05:49 网站建设 项目流程

Cloudflare Turnstile 配置完全指南:脚本加载、组件选项与框架集成的实战手册(Codex Skills 仓库深度解析)

【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills

本篇技术指南以本仓库 Codex Skills 目录中 cloudflare-deploy 技能下的 Turnstile 配置参考文档 为核心骨架,系统讲解 Cloudflare Turnstile(一种无需用户手动拼图/勾选的人机验证方案)的前端配置全流程:从四种脚本加载方式、完整的组件 Options 对象、HTML 数据属性映射,到 Content Security Policy 与 React / Vue / Svelte / Next.js 框架接入,以及 Cloudflare Pages 官方插件。读完本文,你将能够独立完成 Turnstile 从「引入脚本 → 渲染组件 → 服务端校验 → 框架集成」的完整落地,并规避 token 过期、单次使用、CSP 拦截等常见坑点。

一、Turnstile 是什么:先理解它在整个技能体系中的定位

在本仓库中,cloudflare-deploy 是一个「部署应用到 Cloudflare」的整合型技能,其 SKILL.md 通过决策树引导 Agent 选择合适产品:在 "I need security"(我需要安全能力)分支下,CAPTCHA alternative → turnstile/。也就是说,Turnstile 在本技能中承担的是人机验证/反机器人这一安全职责,与 WAF、DDoS、Bot Management、API Shield 并列。

依据同目录 turnstile README,Turnstile 是一种用户友好的 CAPTCHA 替代品:它在后台运行挑战,无需用户交互即可完成验证,通过浏览器行为、设备指纹与机器学习等信号自动判断访问者是否为真人。配置文档(即本文核心)则负责回答「怎么把验证组件渲染到页面上、怎么配置它的行为」。

在动手配置之前,需要先了解 Turnstile 的三种组件形态(源自 README.md),因为它们直接决定后续配置方式:

类型交互方式适用场景
Managed(托管,默认)仅在需要时显示复选框表单、登录页——兼顾体验与安全
Non-Interactive(非交互)不可见,自动运行无感体验、低风险操作
Invisible(隐形)隐藏,通过代码触发预放行(Pre-clearance)、API 调用、无头环境

同时必须记住三条硬性约束(源自 README 与 gotchas.md):

  • Token 有效期 5 分钟:超过 5 分钟即失效;
  • Token 单次使用:每个 token 只能被 siteverify 校验一次,重复校验会报timeout-or-duplicate
  • 必须服务端校验:仅靠前端校验可被轻易绕过。

二、脚本加载:四种方式各取所需

配置的起点是引入官方脚本,脚本地址统一为https://challenges.cloudflare.com/turnstile/v0/api.js。configuration.md 给出四种加载方式,对应不同的渲染控制粒度。

1. 基础方式(隐式渲染,Implicit Rendering)

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

async defer保证脚本异步加载且不阻塞页面解析。脚本加载后会自动扫描页面中带有class="cf-turnstile"的元素,在页面加载时自动渲染为 Turnstile 组件——这就是「隐式渲染」:无需写任何 JavaScript,只需在表单里放一个<div class="cf-turnstile"><script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"></script>

通过 URL 参数render=explicit关闭自动渲染,改为通过window.turnstile.render()手动控制组件渲染的时机与位置。适用于 SPA、动态插入容器、或需要精确控制渲染时机的场景。

3. 带加载回调(With Load Callback)

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=myCallback"></script> <script> function myCallback() { // API ready window.turnstile.render('#container', { sitekey: 'YOUR_SITE_KEY' }); } </script>

onload参数指定一个全局回调函数名,脚本加载完成、window.turnstileAPI 就绪后立即调用。注意:api.md 中的 TypeScript 声明还支持window.onloadTurnstileCallback这种命名约定,两种写法等价,选择其一保持一致即可。

4. 兼容模式(Compatibility Mode)

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?compat=recaptcha"></script>

加上compat=recaptcha后,脚本会额外提供grecaptchaAPI,让已接入 Google reCAPTCHA 的老代码可以零改造成本地切换到 Turnstile(drop-in replacement)。适合存量项目迁移。

三、组件配置:完整的 Options 对象与关键选项语义

显式渲染(window.turnstile.render(container, options))的核心是 Options 对象。configuration.md 给出了完整的配置结构,下面按「必填 / 回调 / 外观 / 行为 / 表单集成 / 分析与数据」六个维度完整呈现,并补充默认值与取值说明:

{ // —— 必填 —— sitekey: 'YOUR_SITE_KEY', // 从 Cloudflare 控制台创建 Widget 后获得的站点密钥 // —— 回调函数 —— callback: (token) => {}, // 校验成功,token 已就绪(在表单提交前一定要等到它) 'error-callback': (code) => {}, // 发生错误,code 为错误码 'expired-callback': () => {}, // token 过期(>5 分钟) 'timeout-callback': () => {}, // 挑战超时 'before-interactive-callback': () => {}, // 展示复选框之前触发 'after-interactive-callback': () => {}, // 用户交互完成后触发 'unsupported-callback': () => {}, // 浏览器不支持 Turnstile 时触发 // —— 外观 —— theme: 'auto', // 'light' | 'dark' | 'auto'(跟随系统) size: 'normal', // 'normal' | 'compact' | 'flexible' tabindex: 0, // 键盘 Tab 顺序(可访问性) language: 'auto', // ISO 639-1 语言码(如 'en')或 'auto' // —— 行为 —— execution: 'render', // 'render'(渲染即开始挑战)| 'execute'(等 turnstile.execute() 手动触发) appearance: 'always', // 'always' | 'execute' | 'interaction-only' retry: 'auto', // 'auto'(失败自动重试)| 'never' 'retry-interval': 8000, // 重试间隔(毫秒),默认 8000 'refresh-expired': 'auto', // 'auto' | 'manual' | 'never' // —— 表单集成 —— 'response-field': true, // 是否自动生成隐藏 input 存放 token(默认 true) 'response-field-name': 'cf-turnstile-response', // 隐藏 input 的 name,服务端据此取值 // —— 分析与数据 —— action: 'login', // 动作名称,用于分析(siteverify 响应中原样返回) cData: 'user-session-123', // 自定义数据,siteverify 校验时原样返回 }

关键选项深度解读

execution(挑战触发时机)

  • 'render'(默认):组件渲染后挑战立即开始,用户无需等待;
  • 'execute':组件渲染后不自动发起挑战,必须调用window.turnstile.execute()才触发。适合把挑战推迟到用户点击提交按钮之后,配合appearance: 'execute'可实现「先隐藏、提交时才出验证」。

appearance(可见性策略)

  • 'always'(默认):组件始终可见;
  • 'execute':隐藏,直到调用execute()
  • 'interaction-only':隐藏,直到真正需要用户交互时才展示(Managed 类型常用)。

refresh-expired(过期 token 处理)

  • 'auto'(默认):token 过期后自动刷新;
  • 'manual':过期后应用需自行调用turnstile.reset()重置;
  • 'never':不刷新,只触发expired-callback

retry(挑战失败重试)

  • 'auto'(默认):挑战失败自动重试;
  • 'never':不重试,直接触发error-callback

这些选项的取值域与 api.md 中的TurnstileOptionsTypeScript 接口完全一致(execution?: 'render' | 'execute'appearance?: 'always' | 'execute' | 'interaction-only'等),TypeScript 项目可直接获得编译期类型检查。

四、HTML 数据属性:隐式渲染的声明式配置

隐式渲染(<div class="cf-turnstile">)不需要写 JavaScript,所有配置通过 data 属性声明。configuration.md 给出了完整的「JavaScript 属性 ↔ HTML 数据属性」映射表,以下为全量对照:

JavaScript 属性HTML 数据属性示例
sitekeydata-sitekeydata-sitekey="YOUR_KEY"
actiondata-actiondata-action="login"
cDatadata-cdatadata-cdata="session-123"
callbackdata-callbackdata-callback="onSuccess"
error-callbackdata-error-callbackdata-error-callback="onError"
expired-callbackdata-expired-callbackdata-expired-callback="onExpired"
timeout-callbackdata-timeout-callbackdata-timeout-callback="onTimeout"
themedata-themedata-theme="dark"
sizedata-sizedata-size="compact"
tabindexdata-tabindexdata-tabindex="0"
response-fielddata-response-fielddata-response-field="false"
response-field-namedata-response-field-namedata-response-field-name="token"
retrydata-retrydata-retry="never"
retry-intervaldata-retry-intervaldata-retry-interval="5000"
languagedata-languagedata-language="en"
executiondata-executiondata-execution="execute"
appearancedata-appearancedata-appearance="interaction-only"
refresh-expireddata-refresh-expireddata-refresh-expired="manual"

注意数据属性中的回调(如data-callback="onSuccess")指向的是全局函数名,因此隐式渲染要求相关回调函数定义在全局作用域。

完整示例:

<div class="cf-turnstile" ><meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://challenges.cloudflare.com; frame-src https://challenges.cloudflare.com;">

gotchas.md 明确把CSP 拦截列为「Widget 不渲染」的头号原因之一,其余常见原因还包括:sitekey 错误、file://协议下无法加载(应改用http://本地服务)。若组件不渲染,请优先按此排查。

六、框架级接入:React / Vue / Svelte / Next.js

React

官方推荐的社区封装包为@marsidev/react-turnstile

npm install @marsidev/react-turnstile
import Turnstile from '@marsidev/react-turnstile'; <Turnstile siteKey="YOUR_SITE_KEY" onSuccess={(token) => console.log(token)} />

Vue

npm install vue-turnstile
<template> <VueTurnstile site-key="YOUR_SITE_KEY" @success="onSuccess" /> </template> <script setup> import VueTurnstile from 'vue-turnstile'; </script>

Svelte

npm install svelte-turnstile
<script> import Turnstile from 'svelte-turnstile'; </script> <Turnstile siteKey="YOUR_SITE_KEY" on:turnstile-callback={handleToken} />

Next.js(App Router)

由于window.turnstile只在浏览器端存在,必须用'use client'声明客户端组件,并在useEffect中完成渲染与清理(源自 configuration.md):

// app/components/TurnstileWidget.tsx 'use client'; import { useEffect, useRef } from 'react'; export default function TurnstileWidget({ sitekey, onSuccess }) { const ref = useRef<HTMLDivElement>(null); useEffect(() => { if (ref.current && window.turnstile) { const widgetId = window.turnstile.render(ref.current, { sitekey, callback: onSuccess }); return () => window.turnstile.remove(widgetId); } }, [sitekey, onSuccess]); return <div ref={ref} />; }

框架接入的三大坑(源自 gotchas.md)

  1. React 组件重挂载丢 token:组件因 state 变化重新渲染时,验证 token 会丢失。解决思路是用useRef锁定渲染生命周期,仅在首次挂载时render、卸载时remove
  2. React StrictMode 双重渲染:开发模式下 StrictMode 会执行两次 effect,必须提供清理函数(return () => window.turnstile.remove(widgetId))避免出现两个叠加的组件实例;
  3. Next.js SSR 水合问题window.turnstile在服务端渲染阶段为undefined,组件必须'use client',或使用dynamic(..., { ssr: false })动态导入。

七、Cloudflare Pages 插件:一行代码接入服务端校验

如果站点部署在 Cloudflare Pages,可以直接使用官方中间件插件,把验证逻辑放进 Functions 中间件,无需手写 siteverify 调用(源自 configuration.md):

npm install @cloudflare/pages-plugin-turnstile
// functions/_middleware.ts import turnstilePlugin from '@cloudflare/pages-plugin-turnstile'; export const onRequest = turnstilePlugin({ secret: 'YOUR_SECRET_KEY', onError: () => new Response('CAPTCHA failed', { status: 403 }) });

只要请求通过_middleware.ts,插件就会自动校验请求携带的 Turnstile token,校验失败直接返回 403。注意:secret是服务端密钥,绝不能出现在客户端代码中(详见下文安全底线)。

八、配合服务端校验:让配置真正闭环

配置文档负责「前端渲染」,但人机验证的安全闭环必须落在服务端。结合 api.md 与 patterns.md,siteverify 的完整流程如下:

端点POST https://challenges.cloudflare.com/turnstile/v0/siteverify

请求体字段

字段类型说明
secretstring你的密钥(绝不暴露给客户端)
responsestring前端提交的 token(来自cf-turnstile-response隐藏字段)
remoteipstring用户 IP(可选但建议传)
idempotency_keystring幂等校验唯一键(可选)

响应字段success(校验结果)、challenge_ts(挑战时间戳 ISO)、hostname(解决验证的域名)、error-codes(失败时的错误码数组)、action(组件配置的动作名)、cdata(组件配置的自定义数据)。

Cloudflare Workers 服务端校验示例(源自 README 与 patterns.md):

export default { async fetch(request) { const formData = await request.formData(); const token = formData.get('cf-turnstile-response'); const result = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ secret: env.TURNSTILE_SECRET, response: token, remoteip: request.headers.get('CF-Connecting-IP') }) }); const validation = await result.json(); if (!validation.success) { return new Response('Invalid CAPTCHA', { status: 400 }); } // Process form... } }

常见错误码速查(源自 api.md):

错误码原因处理
missing-input-secret未提供 secret请求中带上secret
invalid-input-secretsecret 错误在控制台核对密钥
missing-input-response未提供 token带上responsetoken
invalid-input-responsetoken 非法/格式错误确认来自组件的 token
timeout-or-duplicatetoken 过期(>5 分钟)或重复使用生成新 token,只能校验一次
internal-errorCloudflare 服务端错误指数退避重试
bad-request请求格式错误检查 JSON/表单编码

四条不可妥协的安全底线(源自 gotchas.md):

  1. 禁止只做前端校验:客户端校验可被轻易绕过,必须服务端调用 siteverify;
  2. 禁止暴露 secret:密钥只存在于服务端环境变量,绝不写入前端代码;
  3. 禁止复用 token:token 单次有效,提交失败后调用window.turnstile.reset(widgetId)生成新 token;
  4. 必须处理过期:配置refresh-expired: 'auto',或在expired-callback中重置组件。

环境区分测试密钥(源自 README 与 patterns.md,测试密钥在 localhost 与任意域名均可用,严禁用于生产):

类型密钥行为
Site Key(始终通过)1x00000000000000000000AA组件成功,token 可通过校验
Site Key(始终拦截)2x00000000000000000000AB组件可见地失败
Site Key(强制挑战)3x00000000000000000000FF总是展示交互挑战
Secret Key(测试)1x0000000000000000000000000000000AA校验测试 token

生产/测试切换建议按环境变量区分:

const SITE_KEY = process.env.NODE_ENV === 'production' ? process.env.TURNSTILE_SITE_KEY : '1x00000000000000000000AA';

九、调试三板斧与约束速查

调试建议(源自 gotchas.md):

  • 组件渲染时同时注册callback/error-callback/expired-callback/timeout-callback并输出日志,第一时间定位状态;
  • window.turnstile.getResponse(widgetId)检查 token 是否就绪,用window.turnstile.isExpired(widgetId)检查是否过期;
  • 打开浏览器 Network 面板:确认api.js返回 200、观察 siteverify 请求/响应、排查 4xx/5xx。

约束速查表:

约束影响
Token 有效期5 分钟过期需重新生成
Token 使用次数单次不能重复校验同一 token
组件尺寸normal 300×65px;compact 130×120px布局时预留空间

十、总结

Turnstile 的配置本质上是一条「加载脚本 → 渲染组件(隐式/显式)→ 收集 token → 服务端 siteverify 校验」的闭环链路:configuration.md 解决了链路的前半段(脚本加载、Options 配置、数据属性、CSP、框架接入、Pages 插件),而后半段的校验细节可继续阅读同目录的 api.md(客户端 API 与 siteverify 接口)、patterns.md(表单集成与预放行模式)、gotchas.md(排错与反模式)。把「5 分钟过期、单次使用、必须服务端校验、密钥不出服务端」这四条底线刻在脑海里,你的 Turnstile 接入就能既顺滑又安全。

【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询