用 Cloudflare 加速 AI 应用:缓存、API 限速与回源保护怎么配
用 Cloudflare 加速 AI 应用,实际能拿到的是这些:静态资源和少量只读接口在边缘缓存,聊天接口在到达源站之前先被限速,源站 IP 不再直接暴露。流式输出则要多处理一步:对话接口必须绕过缓存,响应头要尽早发出,长时间无输出时要发心跳,否则容易遇到 524 超时或"一次性吐出全部内容"的假流式。下面按缓存、限速、回源保护、SSE 的顺序给出可以照做的配置。
AI 应用哪些接口能缓存,哪些不能
AI 应用的流量可以按下表归类,处理方式各不相同。Cloudflare 默认只缓存按扩展名识别的静态文件,HTML 和 JSON 接口默认不缓存,所以"能缓存的接口"需要你主动用缓存规则(Cache Rules)打开,"不能缓存的接口"则要确保没有被误开。
| 类型 | 例子 | 建议 | 说明 |
|---|---|---|---|
| 静态资源 | JS、CSS、字体、图片、前端构建产物 | 长缓存,文件名带哈希 | 边缘 TTL 可设很长,发版靠改文件名 |
| 公共只读接口 | 模型列表、价格表、公开配置、文档页 | 短缓存,几十秒到几分钟 | 前提是所有用户看到的内容相同 |
| 按用户区分的只读接口 | 当前用户的额度、会话列表 | 不缓存,或在 Worker 里把用户标识放进缓存键 | 缓存键漏掉用户维度会把 A 的数据给 B |
| 生成类接口 | 对话、补全、Embedding、图片生成 | 不缓存 | 每次请求不同,且多为 POST 和流式 |
| 鉴权与计费 | 登录、回调、支付通知 | 不缓存,不改写 | 只做限速和来源校验 |
判断一个接口能不能缓存,问自己两个问题:响应是否只取决于 URL;同一个 URL 给任何人看都没问题。两个答案都是"是"才缓存。模型列表接口要特别留意:如果它会按 API Key 返回不同的可用模型,就属于表中“按用户区分的只读接口”。
缓存规则示例
在 Cloudflare 控制台的 Caching → Cache Rules 里建两条规则,顺序如下:
- 生成类接口绕过缓存
表达式:
starts_with(http.request.uri.path, "/v1/chat") or starts_with(http.request.uri.path, "/v1/completions") or starts_with(http.request.uri.path, "/api/stream")动作:Bypass cache。 - 公共只读接口短缓存
表达式:
http.request.method eq "GET" and http.request.uri.path in {"/v1/models" "/api/pricing"}动作:Eligible for cache;Edge TTL 设为 60 秒,忽略源站的 Cache-Control。
路径按你自己的应用改。第一条虽然看起来多余(POST 本来就不会被缓存),但写明之后,日后有人加"缓存所有 /api"的规则时不容易误伤。
API 限速怎么做:边缘按 IP,Worker 按用户
AI 接口的单次成本远高于普通接口,被刷的后果是账单而不是带宽。限速建议分两层:边缘按 IP 粗挡,Worker 或源站按用户精挡。
第一层:WAF 限速规则
在 Security → WAF → Rate limiting rules 里新建规则,例如:
- 匹配:
http.request.method eq "POST" and starts_with(http.request.uri.path, "/v1/chat") - 计数维度:IP
- 阈值:假设每 10 秒 20 次(示例值,按你的真实使用调整)
- 动作:Block 或 Managed Challenge,持续一段时间
浏览器端调用的接口可以用 Managed Challenge;给 SDK 或命令行工具用的接口不要用 Challenge,脚本无法完成质询,只会得到一堆 403,应直接返回 429。可用的计数维度、时间窗口和规则数量与套餐有关,以 Cloudflare 官方文档为准。
第二层:Worker 按用户限速
按 IP 限速挡不住"一个账号换很多 IP",也会误伤公司、学校这类共用出口的用户。按 API Key 或用户 ID 限速更准确。Workers 的 Rate Limiting 绑定可以直接做到:
# wrangler.toml
name = "ai-edge"
main = "src/index.js"
compatibility_date = "2026-10-01"
[[ratelimits]]
name = "USER_LIMITER"
namespace_id = "1001"
simple = { limit = 30, period = 60 } # 每个 key 每 60 秒 30 次,示例值
// src/index.js
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname.startsWith("/v1/chat")) {
const auth = request.headers.get("Authorization") || "";
if (!auth) return new Response("missing key", { status: 401 });
// 用 key 的哈希做限速键,避免明文 key 出现在日志里
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(auth));
const key = btoa(String.fromCharCode(...new Uint8Array(digest))).slice(0, 24);
const { success } = await env.USER_LIMITER.limit({ key });
if (!success) {
return new Response(JSON.stringify({ error: "rate_limited" }), {
status: 429,
headers: { "Content-Type": "application/json", "Retry-After": "60" },
});
}
}
// 转发到源站,并带上只有边缘知道的共享密钥
const upstream = new Request(request);
upstream.headers.set("X-Edge-Secret", env.EDGE_SECRET);
const resp = await fetch(upstream);
// 直接把 body 流交回去,不要 await resp.text(),否则流式会变成一次性返回
return new Response(resp.body, resp);
},
};
这个绑定的计数是"按边缘位置近似"的,适合挡滥用,不适合做精确计费。额度、余额这类要求准确的限制,仍然放在源站或数据库里做。EDGE_SECRET 用 wrangler secret put EDGE_SECRET 写入,不要写进代码仓库。
注册和免费试用入口还可以加 Turnstile 人机验证,配合邮箱验证,比单纯的 IP 限速更能挡住批量注册。
怎样让源站只接受 Cloudflare 的流量
只把域名接入 Cloudflare 而不收紧源站,攻击者找到源站 IP 后可以绕过所有边缘规则直接打。下面几种做法可以组合使用,按实施成本从低到高排列:
| 方式 | 做法 | 能防住什么 |
|---|---|---|
| 共享密钥头 | Worker 或 Transform Rule 加 X-Edge-Secret,源站校验 | 直连源站的请求被拒(密钥需定期轮换) |
| IP 白名单 | 源站防火墙只放行 Cloudflare 公布的 IP 段(cloudflare.com/ips) | 非 Cloudflare 来源的连接 |
| Authenticated Origin Pulls | 源站要求 Cloudflare 出示客户端证书(mTLS) | 非 Cloudflare 来源的连接;换成你自己上传的证书后,还能挡住别人用自己的 Cloudflare 账号把流量指向你的源站 |
| Cloudflare Tunnel | 源站主动向外建隧道,不开放任何入站端口 | 源站完全不暴露公网 IP |
IP 白名单有一个常被忽略的漏洞:任何 Cloudflare 用户的流量都来自同一批 IP 段,所以白名单最好与共享密钥或 mTLS 一起用。另外,源站换 IP 前先查一下历史 DNS 记录和邮件头里是否泄露过旧 IP,旧 IP 泄露后单靠接入 Cloudflare 起不到隐藏作用。
SSE 流式输出为什么会卡住或超时
经过 Cloudflare 代理后,流式对话常见的问题有三个。
1. 被缓冲,变成一次性返回
Cloudflare 代理对 text/event-stream 会边收边转,问题通常出在链路的其他环节:
- 源站前面的 Nginx 默认会缓冲上游响应。在对应 location 里加
proxy_buffering off;,或让应用返回X-Accel-Buffering: no响应头。 - 应用框架的压缩中间件可能等攒够数据才输出,流式路由上关掉压缩。
- 响应头建议带上
Content-Type: text/event-stream、Cache-Control: no-cache, no-transform。 - Worker 里如果读取了整个 body(
await resp.text()、resp.json())再返回,流式同样会失效。要改写内容,用TransformStream逐块处理。
2. 524 超时
Cloudflare 等待源站返回响应的时间有上限,免费和多数付费套餐是 100 秒(以官方文档为准),超时后返回 524。对 AI 应用来说,风险点在"首个 token 出来之前":长上下文、推理时间长的请求可能迟迟不发任何数据。做法是收到请求后立刻返回 200 和响应头,然后先发一个 SSE 注释行,之后每 15~30 秒发一次心跳:
: ping
以冒号开头的行是 SSE 注释,标准的客户端会忽略。确实需要超过上限的非流式长任务,改成"提交任务 + 轮询结果"或用队列处理,不要指望延长超时。
3. 客户端断开后上游还在跑
用户关掉页面,边缘连接断开,但源站对上游模型的请求可能继续生成并计费。源站要监听连接关闭事件并取消上游请求(例如在 Node.js 里把 AbortController 的 signal 传给上游 fetch,在请求的 close 事件里调用 abort)。
上线前检查表
接入后用下面的命令逐项确认,几分钟就能跑完:
# 1. 静态资源是否命中缓存(第二次请求应看到 cf-cache-status: HIT)
curl -sI https://your.app/assets/app.3f9c1a.js | grep -i cf-cache-status
# 2. 对话接口是否绕过缓存(应为 BYPASS 或 DYNAMIC,不应出现 HIT)
curl -sI -X POST https://your.app/v1/chat -H "Authorization: Bearer test" | grep -i cf-cache-status
# 3. 流式是否逐块到达(-N 关闭 curl 自身缓冲,输出应一行行出现)
curl -N https://your.app/v1/chat -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -d '{"stream":true,"messages":[{"role":"user","content":"数到 20"}]}'
# 4. 限速是否生效(连续请求,后面应出现 429)
for i in $(seq 1 40); do curl -s -o /dev/null -w "%{http_code}\n" -X POST https://your.app/v1/chat -H "Authorization: Bearer test"; done | sort | uniq -c
# 5. 源站是否拒绝直连(应超时、403 或连接被拒)
curl -sI --resolve your.app:443:<源站IP> https://your.app/
| 检查项 | 预期结果 |
|---|---|
| 带哈希的静态文件 | 第二次请求 HIT |
| 模型列表等公共接口 | HIT,且 TTL 到期后能刷新 |
| 对话、补全接口 | 从不出现 HIT |
| 流式输出 | 逐块出现,长请求不出 524 |
| 超出限速 | 返回 429 并带 Retry-After |
| 直连源站 IP | 被拒绝 |
Cloudflare 加速管不到哪一段
边缘只能优化"用户到边缘"和"边缘到你的源站",源站再去调用模型 API 的那一段仍然取决于源站位置。作为参考,cdnping 在 2026 年 10 月 17 日(UTC)从 Cloudflare 丹佛(DEN)节点对各官方 API 入口做的探测(每 5 分钟一次,当天窗口约 85 分钟,来源 /status)中,api.anthropic.com 的中位数为 73 ms,api.openai.com 为 96 ms,而 api.deepseek.com 为 353 ms、open.bigmodel.cn 为 395 ms。数据窗口很短,只能说明一个方向:源站离哪家上游更近,就该部署在哪边,边缘层不能替代这个选择。如果你的源站调用的是聚合网关,例如 AGI Plan(AGI Plan 是 cdnping 的商业合作方),可以在 /status/agiplan 看它的探测和公开可用率。
另一个现实问题是中国大陆的访问。Cloudflare 的国际网络对大陆用户的延迟因运营商和地区差别很大;在大陆境内使用节点加速需要 ICP 备案,并经由持证服务商接入。接入前可以先用 cdnping 的在线测速看自己网络到各家 CDN 和 API 的首次连接与往返耗时,再参考 CDN 速度榜、国内如何选择公共 CDN 和 国内调用 AI API 的延迟。
下一步
- 按第一节的表格,把你的路由逐条归类,写出两条缓存规则。
- 给对话接口加一条 WAF 限速规则,再用上面的 Worker 按 key 限速。
- 跑一遍检查表,重点看流式输出和直连源站这两项。
- 如果不想自己维护这套配置,cdnping Edge 提供基于 Cloudflare 全球网络的边缘加速服务,按需报价,提交邮箱后 1 个工作日内联系;涉及大陆境内加速的部分同样需要备案。