Cloudflare REST API 集成完全指南:认证、SDK 配置、限流与实战模式(cloudflare-deploy Skill)
【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills
本篇指南以 cloudflare-deploy Skill 中 api 参考目录 为核心骨架,系统讲解如何通过 Cloudflare REST API 完成认证、SDK 客户端初始化、自动分页、错误处理、Zone 与 DNS 管理等开发工作。读完你将掌握 TypeScript / Python / Go 三种官方 SDK 的初始化与配置方法、API Token 与 API Key 两种认证方式的取舍、速率限制规避策略,以及批量操作、错误恢复等可复用的实战代码模式。
何时使用 REST API:先看决策树
Cloudflare 提供 REST API、Workers 运行时 Bindings、CLI(Wrangler)和基础设施即代码(IaC)等多种编程接口,选用前应先根据调用场景做判断。原文档给出的决策树如下:
How are you calling the Cloudflare API? ├─ From Workers runtime → Use bindings, not REST API (see ../bindings/) ├─ Server-side (Node/Python/Go) → Official SDK (see api.md) ├─ CLI/scripts → Wrangler or curl (see configuration.md) ├─ Infrastructure-as-code → See ../pulumi/ or ../terraform/ └─ One-off requests → curl examples (see api.md)要点归纳:
- Workers 运行时内优先使用 Bindings,而非 REST API。Bindings 在运行时零开销、不消耗 API 速率配额,是 Workers 场景下的首选(详见下文“Workers 子请求”一节)。
- 服务端应用(Node/Python/Go)使用官方 SDK,见 api.md。
- 脚本与 CLI 场景使用 Wrangler 或 curl,配置方法见 configuration.md。
- 基础设施即代码转向 Pulumi 或 Terraform 参考。
- 一次性临时请求直接用 curl 示例即可。
SDK 选型:三种语言如何选择
Cloudflare 官方提供 TypeScript、Python、Go 三套 SDK,全部由 Stainless 根据 OpenAPI 规范生成,因此三者的 API 形态高度一致,学习成本可跨语言复用。选型对照如下:
| 语言 | 包名 | 最适合场景 | 默认重试次数 |
|---|---|---|---|
| TypeScript | cloudflare | Node.js、Bun、Next.js、Workers | 2 |
| Python | cloudflare | FastAPI、Django、脚本 | 2 |
| Go | cloudflare-go/v4 | CLI 工具、微服务 | 10 |
需要注意:Go SDK 的默认重试次数(10 次)明显高于 TypeScript / Python(2 次),这是三套 SDK 之间最值得留意的行为差异(见 configuration.md)。
认证方式:API Token 是唯一推荐项
| 方法 | 安全性 | 使用场景 | 权限范围 |
|---|---|---|---|
| API Token(推荐) | 可限定权限、可轮换 | 生产环境 | 按 Zone 或按账户 |
| API Key + Email | 拥有完整账户权限,无法细化 | 仅限遗留系统 | 全部资源 |
| User Service Key | 受限 | 仅用于 Origin CA 证书 | Origin CA |
新项目一律使用 API Token。原文档强调 Token 始终遵循最小权限原则(zone 级、限时有效)。创建入口为:Dashboard → My Profile → API Tokens → Create Token。
创建 Token 后,可通过环境变量或 curl 立即验证:
export CLOUDFLARE_API_TOKEN='your-token-here' curl "https://api.cloudflare.com/client/v4/zones" \ --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"API Key 方式(仅遗留场景)使用X-Auth-Email与X-Auth-Key两个请求头:
curl "https://api.cloudflare.com/client/v4/zones" \ --header "X-Auth-Email: user@example.com" \ --header "X-Auth-Key: $CLOUDFLARE_API_KEY"不推荐 API Key:它拥有完整账户访问权限,无法按资源范围收缩权限。关于 Token 权限不足导致的 403 错误排查,见下文“Token 权限不足(403)”一节。
客户端初始化:三语对照
TypeScript
import Cloudflare from 'cloudflare'; const client = new Cloudflare({ apiToken: process.env.CLOUDFLARE_API_TOKEN, });Python
from cloudflare import Cloudflare client = Cloudflare(api_token=os.environ.get("CLOUDFLARE_API_TOKEN")) # 异步场景使用 AsyncCloudflare: from cloudflare import AsyncCloudflare client = AsyncCloudflare(api_token=os.environ["CLOUDFLARE_API_TOKEN"])Go
import ( "github.com/cloudflare/cloudflare-go/v4" "github.com/cloudflare/cloudflare-go/v4/option" ) client := cloudflare.NewClient( option.WithAPIToken(os.Getenv("CLOUDFLARE_API_TOKEN")), )Go 版本要求使用cloudflare-go/v4模块,并配合option包以函数式选项注入 API Token。
SDK 配置:环境变量、超时与重试
环境变量与 .env
| 平台 | 命令 |
|---|---|
| Linux/macOS | export CLOUDFLARE_API_TOKEN='token' |
| PowerShell | $env:CLOUDFLARE_API_TOKEN = 'token' |
| Windows CMD | set CLOUDFLARE_API_TOKEN=token |
安全铁律:绝不把 Token 提交进版本库。使用.env文件(加入.gitignore)或密钥管理服务。
# .env(记得加入 .gitignore) CLOUDFLARE_API_TOKEN=your-token-here CLOUDFLARE_ACCOUNT_ID=your-account-id// TypeScript import 'dotenv/config'; const client = new Cloudflare({ apiToken: process.env.CLOUDFLARE_API_TOKEN, });# Python from dotenv import load_dotenv load_dotenv() client = Cloudflare(api_token=os.environ["CLOUDFLARE_API_TOKEN"])核心配置项
| 选项 | TypeScript | Python | Go | 默认值 |
|---|---|---|---|---|
| 超时 | timeout(毫秒) | timeout(秒) | WithRequestTimeout | 60s |
| 重试 | maxRetries | max_retries | WithMaxRetries | 2(Go 为 10) |
| Base URL | baseURL | base_url | WithBaseURL | api.cloudflare.com |
三语配置示例:
const client = new Cloudflare({ apiToken: process.env.CLOUDFLARE_API_TOKEN, timeout: 120000, // 2 分钟(默认 60s),单位为毫秒 maxRetries: 5, // 默认 2 baseURL: 'https://...', // 代理场景(罕见) }); // 单次请求覆盖配置 await client.zones.get( { zone_id: 'zone-id' }, { timeout: 5000, maxRetries: 0 } );client = Cloudflare( api_token=os.environ["CLOUDFLARE_API_TOKEN"], timeout=120, # 秒(默认 60) max_retries=5, # 默认 2 base_url="https://...", # 代理场景(罕见) ) # 单次请求覆盖配置 client.with_options(timeout=5, max_retries=0).zones.get(zone_id="zone-id")client := cloudflare.NewClient( option.WithAPIToken(os.Getenv("CLOUDFLARE_API_TOKEN")), option.WithMaxRetries(5), // 默认 10(高于 TS/Python) option.WithRequestTimeout(2 * time.Minute), // 默认 60s option.WithBaseURL("https://..."), // 代理场景(罕见) ) // 单次请求覆盖配置 client.Zones.Get(ctx, "zone-id", option.WithMaxRetries(0))何时调整超时与重试
需要调高超时的场景:大型 Zone 迁移、批量 DNS 操作、Worker 脚本上传,例如:
const client = new Cloudflare({ timeout: 300000, // 5 分钟 });重试策略:
- 调高重试:限流密集的工作流、网络不稳定环境(
maxRetries: 10)。 - 调低或关闭重试:需要快速失败的场景、面向用户的前端请求(
maxRetries: 0)。
// 批量操作提高重试 const client = new Cloudflare({ maxRetries: 10 }); // 快速失败场景关闭重试 const fastClient = new Cloudflare({ maxRetries: 0 });速率限制:配额与 SDK 自动重试
原文档给出的限制基线(详见 gotchas.md):
| 限制项 | 数值 |
|---|---|
| 每用户/Token | 1200 次请求 / 5 分钟 |
| 每 IP | 200 次请求 / 秒 |
| GraphQL | 320 / 5 分钟(基于成本计费) |
SDK 内置行为:
- 指数退避自动重试(默认 2 次,Go 为 10 次);
- 尊重
Retry-After响应头; - 重试耗尽后抛出
RateLimitError(429)。
自动分页与错误处理
自动分页
所有官方 SDK 对 list 类操作都内置自动分页,无需手工翻页:
// TypeScript:for await...of for await (const zone of client.zones.list()) { console.log(zone.id); }# Python:迭代器协议 for zone in client.zones.list(): print(zone.id)// Go:ListAutoPaging iter := client.Zones.ListAutoPaging(ctx, cloudflare.ZoneListParams{}) for iter.Next() { zone := iter.Current() fmt.Println(zone.ID) }分页截断陷阱:直接await client.zones.list()只返回第一页(默认每页 20 条)。要拿到全部结果,必须用自动分页迭代器遍历(见下文“常见坑”小节)。
错误类型与处理
try { const zone = await client.zones.get({ zone_id: 'xxx' }); } catch (err) { if (err instanceof Cloudflare.NotFoundError) { // 404 } else if (err instanceof Cloudflare.RateLimitError) { // 429 - SDK 已自动退避重试 } else if (err instanceof Cloudflare.APIError) { console.log(err.status, err.message); } }常用错误类型对照:
| 错误类型 | HTTP 状态 | 含义 |
|---|---|---|
AuthenticationError | 401 | Token 无效 |
PermissionDeniedError | 403 | 权限范围不足 |
NotFoundError | 404 | 资源不存在 |
RateLimitError | 429 | 触发速率限制 |
InternalServerError | ≥500 | Cloudflare 服务端错误 |
核心操作:Zone 管理与 DNS 管理
Zone 生命周期(创建、读取、更新、删除)
// 列出 Zone const zones = await client.zones.list({ account: { id: 'account-id' }, status: 'active', }); // 创建 Zone const zone = await client.zones.create({ account: { id: 'account-id' }, name: 'example.com', type: 'full', // 或 'partial' }); // 更新 Zone await client.zones.edit('zone-id', { paused: false, }); // 删除 Zone await client.zones.delete('zone-id');Go 版本注意必须使用cloudflare.F()包装器包裹字段:
// Go:需要 cloudflare.F() 包装 zone, err := client.Zones.New(ctx, cloudflare.ZoneNewParams{ Account: cloudflare.F(cloudflare.ZoneNewParamsAccount{ ID: cloudflare.F("account-id"), }), Name: cloudflare.F("example.com"), Type: cloudflare.F(cloudflare.ZoneNewParamsTypeFull), })DNS 记录管理
// 创建 DNS 记录 await client.dns.records.create({ zone_id: 'zone-id', type: 'A', name: 'subdomain.example.com', content: '192.0.2.1', ttl: 1, // auto proxied: true, // 橙色云朵(开启代理) }); // 列出 DNS 记录(自动分页) for await (const record of client.dns.records.list({ zone_id: 'zone-id', type: 'A', })) { console.log(record.name, record.content); } // 更新 DNS 记录 await client.dns.records.update({ zone_id: 'zone-id', dns_record_id: 'record-id', type: 'A', name: 'subdomain.example.com', content: '203.0.113.1', proxied: true, }); // 删除 DNS 记录 await client.dns.records.delete({ zone_id: 'zone-id', dns_record_id: 'record-id', });# Python 示例 client.dns.records.create( zone_id="zone-id", type="A", name="subdomain.example.com", content="192.0.2.1", ttl=1, proxied=True, )实战模式:可复用的代码骨架
以下模式均来自 patterns.md,可直接迁移到生产代码。
限流场景下的重试与并发控制
// 提高重试次数应对限流 const client = new Cloudflare({ maxRetries: 5 }); try { const zone = await client.zones.create({ /* ... */ }); } catch (err) { if (err instanceof Cloudflare.RateLimitError) { // 已经过 5 次退避重试仍失败 const retryAfter = err.headers['retry-after']; console.log(`Rate limited. Retry after ${retryAfter}s`); } }批量并行创建(配合并发上限)
// 并行创建多个 DNS 记录(注意限流) const records = ['www', 'api', 'cdn'].map(subdomain => client.dns.records.create({ zone_id: 'zone-id', type: 'A', name: `${subdomain}.example.com`, content: '192.0.2.1', }) ); await Promise.all(records);控制并发(推荐并行数低于 10,避免打满 API 配额):
import pLimit from 'p-limit'; const limit = pLimit(10); // 最多 10 个并发 const subdomains = ['www', 'api', 'cdn', /* 更多 */]; const records = subdomains.map(subdomain => limit(() => client.dns.records.create({ zone_id: 'zone-id', type: 'A', name: `${subdomain}.example.com`, content: '192.0.2.1', })) ); await Promise.all(records);Zone CRUD 工作流
// 创建 const zone = await client.zones.create({ account: { id: 'account-id' }, name: 'example.com', type: 'full', }); // 读取 const fetched = await client.zones.get({ zone_id: zone.id }); // 更新 await client.zones.edit(zone.id, { paused: false }); // 删除 await client.zones.delete(zone.id);DNS 批量更新(全量 A 记录指向新 IP)
// 拉取全部 A 记录 const records = []; for await (const record of client.dns.records.list({ zone_id: 'zone-id', type: 'A', })) { records.push(record); } // 全部更新到新 IP await Promise.all(records.map(record => client.dns.records.update({ zone_id: 'zone-id', dns_record_id: record.id, type: 'A', name: record.name, content: '203.0.113.1', // 新 IP proxied: record.proxied, ttl: record.ttl, }) ));过滤收集结果
// 找出所有已开启代理的 A 记录 const proxiedRecords = []; for await (const record of client.dns.records.list({ zone_id: 'zone-id', type: 'A', })) { if (record.proxied) { proxiedRecords.push(record); } }错误恢复:限流重试函数
async function createZoneWithRetry(name: string, maxAttempts = 3) { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await client.zones.create({ account: { id: 'account-id' }, name, type: 'full', }); } catch (err) { if (err instanceof Cloudflare.RateLimitError && attempt < maxAttempts) { const retryAfter = parseInt(err.headers['retry-after'] || '5'); console.log(`Rate limited, waiting ${retryAfter}s (retry ${attempt}/${maxAttempts})`); await new Promise(resolve => setTimeout(resolve, retryAfter * 1000)); } else { throw err; } } } }条件更新与批量容错
// 仅当 Zone 处于 active 状态才更新 const zone = await client.zones.get({ zone_id: 'zone-id' }); if (zone.status === 'active') { await client.zones.edit(zone.id, { paused: false }); }// 批量处理多个 Zone,单个失败不中断整体 const results = await Promise.allSettled( zoneIds.map(id => client.zones.get({ zone_id: id })) ); results.forEach((result, i) => { if (result.status === 'fulfilled') { console.log(`Zone ${i}: ${result.value.name}`); } else { console.error(`Zone ${i} failed:`, result.reason.message); } });常见坑与排错
本节汇总 gotchas.md 中的高频问题与解决方案。
Go:必填字段包装器cloudflare.F()
Go SDK 为区分“零值 / null / 未传字段”,要求可选字段必须用cloudflare.F()包装:
// 错误写法:不编译或字段不会发送 client.Zones.New(ctx, cloudflare.ZoneNewParams{ Name: "example.com", }) // 正确写法 client.Zones.New(ctx, cloudflare.ZoneNewParams{ Name: cloudflare.F("example.com"), Account: cloudflare.F(cloudflare.ZoneNewParamsAccount{ ID: cloudflare.F("account-id"), }), })Python:同步与异步客户端混用
# 错误写法:同步客户端不能 await from cloudflare import Cloudflare client = Cloudflare() await client.zones.list() # TypeError # 正确写法:使用 AsyncCloudflare from cloudflare import AsyncCloudflare client = AsyncCloudflare() await client.zones.list()Token 权限不足(403)
Token 本身有效但返回 403,通常是权限范围不足。常见操作所需权限如下:
| 操作 | 所需权限 |
|---|---|
| 列出 Zone | Zone:Read(Zone 级或账户级) |
| 创建 Zone | Zone:Edit(账户级) |
| 编辑 DNS | DNS:Edit(Zone 级) |
| 部署 Worker | Workers Script:Edit(账户级) |
| 读取 KV | Workers KV Storage:Read |
| 写入 KV | Workers KV Storage:Edit |
解决:Dashboard → My Profile → API Tokens 中重建带正确权限的 Token。
分页截断:只拿到前 20 条
// 错误写法:只有第一页(20 条) const page = await client.zones.list(); // 正确写法:拿到全部结果 const zones = []; for await (const zone of client.zones.list()) { zones.push(zone); }Workers 子请求:更快触达限流
在 Workers 运行时内直接调用 REST API,每次调用都计入速率配额,比预期更快触发限流。正确做法是改用 Bindings,绑定访问不计入 API 限流:
// 错误写法:Workers 内走 REST API(计入限流) const client = new Cloudflare({ apiToken: env.CLOUDFLARE_API_TOKEN }); const zones = await client.zones.list(); // 正确写法:使用绑定(无限流) // 通过 env.MY_BINDING 访问认证失败(401)
可能原因:Token 过期、被删除/吊销、环境变量未设置、Token 格式错误。建议在代码入口显式校验 Token,并通过 Token 校验接口自测:
// 校验 Token 已设置 if (!process.env.CLOUDFLARE_API_TOKEN) { throw new Error('CLOUDFLARE_API_TOKEN not set'); } // 测试 Token 有效性 const user = await client.user.tokens.verify(); console.log('Token valid:', user.status);超时(默认 60 秒)
常见于批量 DNS、Zone 迁移等大操作。处理手段:调高超时,或拆分批次:
// 调高超时 const client = new Cloudflare({ timeout: 300000, // 5 分钟 }); // 或分批处理 const batchSize = 100; for (let i = 0; i < records.length; i += batchSize) { const batch = records.slice(i, i + batchSize); await processBatch(batch); }Zone 404:ID 有效却找不到
可能原因:Zone 不在 Token 关联的账户下、Zone 已被删除、Zone ID 格式错误。可先列出全部 Zone 核对 ID:
for await (const zone of client.zones.list()) { console.log(zone.id, zone.name); }限制速查表
| 资源/限制 | 数值 | 说明 |
|---|---|---|
| API 速率限制 | 1200/5min | 按用户/Token |
| IP 速率限制 | 200/sec | 按 IP |
| GraphQL 速率限制 | 320/5min | 基于成本计费 |
| 并行请求(建议) | < 10 | 避免压垮 API |
| 默认页大小 | 20 | 使用自动分页 |
| 最大页大小 | 50 | 部分端点 |
与 Wrangler CLI 协同
REST API 之外的脚本化运维,可借助 Wrangler CLI(Wrangler 参考):
# 配置认证 wrangler login # 或 export CLOUDFLARE_API_TOKEN='token' # 常用 API 相关命令 wrangler deploy # 通过 API 上传 Worker wrangler kv:key put # KV 操作 wrangler r2 bucket create # R2 操作 wrangler d1 execute # D1 操作 wrangler pages deploy # Pages 操作 # 查看认证信息 wrangler whoami # 显示当前认证用户wrangler.toml示例:
name = "my-worker" main = "src/index.ts" compatibility_date = "2024-01-01" account_id = "your-account-id" # 也可用环境变量: # CLOUDFLARE_ACCOUNT_ID # CLOUDFLARE_API_TOKEN在 cloudflare-deploy Skill 的整体流程中,部署前建议先执行npx wrangler whoami验证认证状态(见 SKILL.md)。
最佳实践汇总
安全方面:
- 绝不提交 Token 到版本库;
- 使用最小权限原则;
- 定期轮换 Token;
- 为 Token 设置过期时间。
性能方面:
- 批量操作代替逐个调用;
- 合理使用自动分页;
- 缓存响应;
- 妥善处理限流。
代码组织:
// 创建可复用的客户端单例 export const cfClient = new Cloudflare({ apiToken: process.env.CLOUDFLARE_API_TOKEN, maxRetries: 5, }); // 封装常用操作 export async function getZoneDetails(zoneId: string) { return await cfClient.zones.get({ zone_id: zoneId }); }参考阅读路径
按任务选择合适的文档:
| 任务 | 阅读文件 |
|---|---|
| 初始化 SDK 客户端 | api.md |
| 配置认证/超时/重试 | configuration.md |
| 查找使用模式 | patterns.md |
| 排查错误与限流 | gotchas.md |
| 产品级 API(Workers/R2/KV 等) | workers、r2、kv 等 |
| Workers 运行时绑定(REST API 的替代方案) | bindings |
| Wrangler CLI 细节 | wrangler |
核心结论一句话:服务端应用直接使用官方 SDK 并优先 API Token,Workers 运行时改用 Bindings,脚本运维交给 Wrangler,批量与高并发场景务必做好并发控制与重试策略。
【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考