Cloudflare Turnstile 完整 API 参考:前端 JavaScript API 与 Siteverify 服务端校验实战
2026/9/13 5:10:10 网站建设 项目流程

Cloudflare Turnstile 完整 API 参考:前端 JavaScript API 与 Siteverify 服务端校验实战

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

本文以 skills/.curated/cloudflare-deploy/references/turnstile/api.md 为骨架,系统讲解 Cloudflare Turnstile 的客户端 JavaScript API(window.turnstile)、Siteverify 服务端校验接口、错误码与 TypeScript 类型定义,并结合同目录下的 configuration.md、patterns.md 与 gotchas.md 做纵深补充。读完本文,你将能够在 Cloudflare Workers / Pages Functions 或任意后端环境中完整实现「前端渲染 → 取 token → 服务端校验」的闭环,并掌握 Token 过期、单次使用、CSP 与密钥安全等关键约束。

该文档隶属于本仓库 cloudflare-deploy 技能下的安全产品参考集(Security → Turnstile),是面向 Agent 与开发者的 Turnstile 集成权威参考,属于「CAPTCHA 替代方案」决策分支的落地细节层。

一、Turnstile 是什么:无感验证与两种核心 API

Turnstile 是 Cloudflare 提供的智能 CAPTCHA 替代方案:它在后台基于浏览器行为、设备指纹与机器学习信号自动完成访客验证,用户几乎无感知,不出现传统拼图式验证码。其集成模型由两部分 API 组成:

  1. 客户端 JavaScript API:脚本加载后暴露在window.turnstile上,负责在页面中渲染 widget、生成 token、重置或移除 widget;
  2. Siteverify API(服务端):把客户端生成的 token 连同 secret 发送到https://challenges.cloudflare.com/turnstile/v0/siteverify进行最终校验,这一步是安全闭环的必选项——纯客户端校验可被轻易绕过。

阅读顺序(见 README.md)建议为:configuration(配置)→ api(本文)→ patterns(模式)→ gotchas(排错)。

二、脚本加载方式:从加载到可用的三种姿势

Turnstile 的所有客户端能力都来自api.js,加载方式直接影响渲染时机与兼容性(本文档「Script Loading」一节给出的三种方式):

<!-- 1. 标准加载:页面加载后自动渲染所有 class="cf-turnstile" 的容器(隐式渲染) --> <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script> <!-- 2. 显式渲染模式:只有调用 window.turnstile.render() 才渲染 --> <script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"></script> <!-- 3. 带加载回调:api.js 就绪后触发全局回调 --> <script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback"></script> <script> window.onloadTurnstileCallback = () => { window.turnstile.render('#container', { sitekey: 'YOUR_SITE_KEY' }); }; </script>

此外 configuration.md 还补充了两种加载变体:

<!-- 兼容模式:暴露 grecaptcha API,可作 Google reCAPTCHA 的 drop-in 替代 --> <script src="https://challenges.cloudflare.com/turnstile/v0/api.js?compat=recaptcha"></script>
  • 隐式渲染(Implicit):不加?render=参数,页面加载后自动扫描class="cf-turnstile"元素并按 HTML data 属性渲染,适合快速接入;
  • 显式渲染(Explicit):加?render=explicit,完全由window.turnstile.render()手动控制渲染时机与位置,适合 SPA、条件渲染、受控表单。

三、客户端 JavaScript API 全解:window.turnstile

脚本加载完成后,Turnstile JavaScript API 通过全局对象window.turnstile提供五个核心方法。所有方法都以render()返回的widget ID为操作句柄(部分方法也接受容器元素)。

3.1turnstile.render(container, options)—— 渲染 widget

  • 参数container为 CSS 选择器字符串或 DOM 元素;optionsTurnstileOptions配置对象(详见本文第六节,完整字段见 configuration.md);
  • 返回string类型的 widget ID,供其它 API 方法使用。
const widgetId = window.turnstile.render('#my-container', { sitekey: 'YOUR_SITE_KEY', callback: (token) => console.log('Success:', token), 'error-callback': (code) => console.error('Error:', code) });

3.2turnstile.reset(widgetId)—— 重置 widget

清除已生成的 token、重置挑战状态。典型场景是表单校验失败后强制用户重新验证(因为 token 单次有效且会过期)。

// 表单校验失败时重置 if (!validateForm()) { window.turnstile.reset(widgetId); }

3.3turnstile.remove(widgetId)—— 彻底移除 widget

将 widget 从 DOM 中完全删除。典型场景是 SPA 路由切换、组件卸载时的清理(避免孤儿 widget)。

// 导航清理 window.turnstile.remove(widgetId);

3.4turnstile.getResponse(widgetId)—— 获取当前 token

返回 widget 当前的有效 token(挑战已完成时),否则返回undefined。提交前用它判断是否具备可提交的验证凭据。

const token = window.turnstile.getResponse(widgetId); if (token) { submitForm(token); }

3.5turnstile.isExpired(widgetId)—— 判断 token 是否过期

检查 token 是否已超过 5 分钟有效期,返回布尔值。适合在提交前自检,过期即reset()重新生成。

if (window.turnstile.isExpired(widgetId)) { window.turnstile.reset(widgetId); }

3.6 完整的 TypeScript 接口定义

api.md 给出了Turnstile接口的完整 TS 声明,可直接作为类型依据:

interface Turnstile { render(container: string | HTMLElement, options: TurnstileOptions): string; reset(widgetId: string): void; remove(widgetId: string): void; getResponse(widgetId: string): string | undefined; isExpired(widgetId: string): boolean; execute(container?: string | HTMLElement, options?: TurnstileOptions): void; } declare global { interface Window { turnstile: Turnstile; onloadTurnstileCallback?: () => void; } }

其中execute()配合execution: 'execute'appearance: 'execute'使用,用于「预清除(pre-clearance)」等延迟执行场景(详见 patterns.md)。

四、回调签名(Callback Signatures)

widget 生命周期中的关键节点都通过回调暴露,API 文档给出了七种标准签名:

type TurnstileCallback = (token: string) => void; // 挑战成功,token 就绪 type ErrorCallback = (errorCode: string) => void; // 发生错误(携带错误码) type TimeoutCallback = () => void; // 挑战超时 type ExpiredCallback = () => void; // token 过期(>5 分钟) type BeforeInteractiveCallback = () => void; // 即将展示可交互元素前 type AfterInteractiveCallback = () => void; // 用户交互完成后 type UnsupportedCallback = () => void; // 浏览器不支持 Turnstile

对应到 options 中为:callback'error-callback''timeout-callback''expired-callback''before-interactive-callback''after-interactive-callback''unsupported-callback'。调试期可在每个回调里打日志快速定位问题(见 gotchas.md 的 Console Logging 模式)。

五、Siteverify API(服务端校验)

客户端 token 只是「凭据」,真正的安全判定在服务端完成。

5.1 端点与请求

  • Endpointhttps://challenges.cloudflare.com/turnstile/v0/siteverify
  • Method:POST
  • Content-Typeapplication/jsonapplication/x-www-form-urlencoded

请求体结构:

interface SiteverifyRequest { secret: string; // 你的 secret key,绝不能暴露在客户端 response: string; // 来自表单隐藏域 cf-turnstile-response 的 token remoteip?: string; // 用户 IP(可选但推荐) idempotency_key?: string; // 幂等校验唯一键(可选) }

Cloudflare Workers 中的标准调用示例:

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 data = await result.json();

两个关键工程细节(来自 patterns.md 与 gotchas.md):

  • remoteip 取值:Cloudflare Workers 用CF-Connecting-IP;普通代理后端用X-Forwarded-For的首个 IP;
  • CORS 陷阱:Siteverify禁止在浏览器端调用(会触发 CORS 错误),正确路径是「前端 → 自己的后端 → Cloudflare Siteverify」。

5.2 响应结构

interface SiteverifyResponse { success: boolean; // 校验结果 challenge_ts?: string; // 挑战完成的 ISO 时间戳 hostname?: string; // 解决 widget 时所在的域名 'error-codes'?: string[]; // success=false 时的错误码 action?: string; // 来自 widget 配置的 action 名 cdata?: string; // 来自 widget 配置的自定义数据 }

成功响应示例:

{ "success": true, "challenge_ts": "2024-01-15T10:30:00Z", "hostname": "example.com", "action": "login", "cdata": "user123" }

失败响应示例:

{ "success": false, "error-codes": ["timeout-or-duplicate"] }

5.3 完整的 Workers 校验闭环

将 patterns.md 中的完整示例与 api.md 的接口结合,一个可运行的服务端校验如下:

interface Env { TURNSTILE_SECRET: string; } export default { async fetch(request: Request, env: Env): Promise<Response> { if (request.method !== 'POST') { return new Response('Method not allowed', { status: 405 }); } const formData = await request.formData(); const token = formData.get('cf-turnstile-response'); if (!token) { return new Response('Missing token', { status: 400 }); } const ip = request.headers.get('CF-Connecting-IP'); 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: ip }) }); const validation = await result.json(); if (!validation.success) { return new Response('CAPTCHA validation failed', { status: 403 }); } // 校验通过,继续处理表单… return new Response('Success'); } };

Cloudflare Pages Functions 使用同一模式,通过ctx.env.TURNSTILE_SECRETctx.request取值(见 patterns.md)。此外还可直接用官方插件@cloudflare/pages-plugin-turnstilefunctions/_middleware.ts中一行接入。

六、错误码速查表

Siteverify 返回的error-codes是排错的第一手线索:

CodeCauseSolution
missing-input-secret请求未提供 secret在请求中包含secret字段
invalid-input-secretsecret 错误到 Dashboard 核对 secret key
missing-input-response未提供 token携带responsetoken
invalid-input-responsetoken 无效或格式错误确认为 widget 生成的合法 token
timeout-or-duplicatetoken 过期(>5 分钟)或被重复使用重新生成 token,且只校验一次
internal-errorCloudflare 服务端错误指数退避后重试
bad-request请求格式错误检查 JSON / 表单编码

其中timeout-or-duplicate是最常见的生产错误,根源在于两条硬性约束:token 5 分钟过期token 单次有效(详见 gotchas.md)。不要把 token 缓存超过 5 分钟,也不要对同一 token 重复校验。

七、TurnstileOptions 完整类型与配置语义

api.md 给出的TurnstileOptions完整定义:

interface TurnstileOptions { sitekey: string; action?: string; cData?: string; callback?: (token: string) => void; 'error-callback'?: (errorCode: string) => void; 'expired-callback'?: () => void; 'timeout-callback'?: () => void; 'before-interactive-callback'?: () => void; 'after-interactive-callback'?: () => void; 'unsupported-callback'?: () => void; theme?: 'light' | 'dark' | 'auto'; size?: 'normal' | 'compact' | 'flexible'; tabindex?: number; 'response-field'?: boolean; 'response-field-name'?: string; retry?: 'auto' | 'never'; 'retry-interval'?: number; language?: string; execution?: 'render' | 'execute'; appearance?: 'always' | 'execute' | 'interaction-only'; 'refresh-expired'?: 'auto' | 'manual' | 'never'; }

各关键字段的语义(configuration.md 详述):

  • sitekey(必填):Dashboard 分配的站点 key;
  • action/cData:为业务打标,action标识场景(如login),cData携带自定义数据,两者都会原样出现在 Siteverify 响应中,可用于防重放与审计;
  • execution'render'(默认,渲染后立即开始挑战)或'execute'(等待手动调用turnstile.execute());
  • appearance'always'(默认始终可见)、'execute'execute()前隐藏)、'interaction-only'(仅在需要用户交互时显示);
  • refresh-expired'auto'(默认自动刷新过期 token)、'manual'(过期后应用自行调用reset())、'never'(不刷新,仅触发expired-callback);
  • retry/'retry-interval''auto'(默认自动重试失败挑战,间隔默认 8000ms)或'never'(不重试,触发error-callback);
  • 'response-field'/'response-field-name':是否自动在表单内注入隐藏域,默认true,隐藏域 name 默认为cf-turnstile-response——服务端正是从这个字段名取 token 的,改名后服务端取值需同步修改;
  • theme/size/language/tabindex:外观与无障碍配置,theme支持light/dark/autosize支持normal/compact/flexiblelanguage使用 ISO 639-1 码或auto

隐式渲染:HTML data 属性映射

隐式渲染时,以上 JS 属性通过data-*属性映射(完整映射表见 configuration.md),这里列出常用部分:

JavaScript PropertyHTML Data Attribute示例
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"

示例:

<div class="cf-turnstile" >const SITE_KEY = process.env.NODE_ENV === 'production' ? 'YOUR_PRODUCTION_SITE_KEY' : '1x00000000000000000000AA'; // Always passes const SECRET_KEY = process.env.NODE_ENV === 'production' ? process.env.TURNSTILE_SECRET : '1x0000000000000000000000000000000AA';

8.3 CSP 配置

若站点启用了 Content Security Policy,必须放行 Turnstile 的脚本与 iframe 域:

script-src https://challenges.cloudflare.com; frame-src https://challenges.cloudflare.com;

完整示例(configuration.md):

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

未配置 CSP 是「widget 不渲染」的常见原因之一(gotchas.md)。

九、常见错误与调试清单

9.1 高频问题定位

错误现象原因解决
Widget 不渲染sitekey 错误、CSP 拦截、file://协议核对 sitekey、为 challenges.cloudflare.com 添加 CSP、改用 http://
timeout-or-duplicatetoken 过期或复用生成新 token,不缓存超过 5 分钟
invalid-input-secretsecret 错误从 Dashboard 核对,检查环境变量
missing-input-responsetoken 未随请求发送检查表单字段名是否为cf-turnstile-response

9.2 客户端调试三件套

// 1. 全回调打日志 window.turnstile.render('#container', { sitekey: 'YOUR_SITE_KEY', callback: (token) => console.log('✓ Token:', token), 'error-callback': (code) => console.error('✗ Error:', code), 'expired-callback': () => console.warn('⏱ Expired'), 'timeout-callback': () => console.warn('⏱ Timeout') }); // 2. 检查 token 状态 const token = window.turnstile.getResponse(widgetId); console.log('Token:', token || 'NOT READY'); console.log('Expired:', window.turnstile.isExpired(widgetId));

9.3 常见配置陷阱

  • 密钥错配:sitekey 与 secret 必须来自同一个 widget,混用会直接校验失败;
  • 测试密钥上生产:务必按 8.2 的环境变量方式隔离;
  • 服务端 secret 未加载:检查.env并验证!!process.env.TURNSTILE_SECRET
  • SPA 组件重挂载:React 中因状态变化导致 widget 重渲染会丢失 token,需用useRef控制生命周期并在卸载时remove()(React StrictMode 下要特别注意清理函数)。

9.4 排错顺序建议

  1. 先换用测试密钥1x00000000000000000000AA+1x0000000000000000000000000000000AA排除 key 问题;
  2. 打开 Network 面板确认api.js返回 200、查看 siteverify 请求与响应体;
  3. 检查是否有 4xx/5xx、CORS 或 CSP 拦截。

十、实战要点总结与延伸阅读

把本文的 API 知识点串成最小可用闭环:加载api.jsrender()生成 widget 并拿到 widgetId → 表单提交时getResponse()取 token(或依赖自动注入的cf-turnstile-response隐藏域)→ 后端调用 Siteverify 校验 → 失败或过期则reset()重新挑战,卸载时remove()清理

在此基础上,本仓库还提供了配套参考文档可继续深入:

  • Turnstile 参考目录(Overview、Widget 类型、快速开始、测试密钥)
  • Turnstile 配置指南(完整 options、data 属性映射、React/Vue/Svelte/Next.js 集成、Pages 插件)
  • Turnstile 常用模式(表单集成、预清除、token 刷新、服务端校验完整代码)
  • Turnstile 排错手册(常见错误、框架陷阱、限流约束、调试方法)
  • cloudflare-deploy 技能总览(安全产品决策树,Turnstile 位于 Security 分支)

以上所有文档与示例代码均可直接在仓库内查看与复用,无需额外安装任何依赖。

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

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

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

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

立即咨询