速率限制:免费档一条规则怎么用出效果
Cloudflare 的速率限制规则在免费计划里只有 1 条,可用的匹配字段只有路径,统计周期只有 10 秒。限制看着很紧,但一条规则足够覆盖最容易被刷的接口——前提是你先把阈值算对,而不是凭感觉填个 100。这篇从阈值估算开始,到规则配置、动作选择、以及怎么在事件里确认它真的触发,最后用一段本地跑出来的数据说明为什么 10 秒窗口会带来边界突刺。
前置知识:知道自己的流量长什么样。控制台的 Analytics → HTTP requests 里有按路径的请求量分布,先看一眼再动手。WAF 的整体规则模型见 77-waf-basics-free-rules.md。
1. 免费档与付费档的差别
| 项目 | 免费 | Pro | Business | Enterprise |
|---|---|---|---|---|
| 规则条数 | 1 | 2 | 5 | 100 |
| 表达式可用字段 | 路径、已验证机器人 | 增加 Host、URI、完整 URI、查询串等 | 同 Pro | 增加方法、来源 IP、User-Agent、请求字段 |
| 周期 | 10 秒 | 最长 1 小时 | 最长 1 天 | 最长 1 天 |
| 计数维度 | 同一 IP + 同一路径 | 可自定义特征组合 | 同 Pro | 可自定义 |
| 高级速率限制 | 无 | 无 | 无 | 付费附加 |
免费档拿不到"按 User-Agent 计数"或"按国家计数",也拿不到 1 分钟以上的窗口。这意味着你的规则只能表达一件事:某个路径在 10 秒内被同一个 IP 请求超过 N 次就处理。
2. 阈值怎么算
先把业务峰值换算成 10 秒的口径。一个页面加载会并发 20 到 40 个请求(HTML、JS、CSS、图片、字体),如果一个 IP 打开三个标签页,10 秒内产生 100 次请求是正常的。所以阈值定在 100 以下,正常用户会开始被拦。
计算方法:单用户 10 秒峰值 × 安全系数。单用户峰值取 60,安全系数取 3,阈值就是 180。低于这个数,先去看自己的页面是不是请求太多(图片没做雪碧图、字体文件被重复加载),而不是把阈值调高。
对 API 端点算法不同。假设某个接口约定每个客户端 60 次/分钟,换算成 10 秒就是 10 次,留 50% 余量填 15。客户端如果有重试逻辑,把重试次数算进去。
| 场景 | 10 秒阈值建议 | 动作 |
|---|---|---|
| 页面与静态资源 | 150–300 | Managed Challenge |
| 登录、注册接口 | 5–10 | Block |
| 公开 API(有文档约定) | 约定值 × 1.5 | Block 或返回 429 |
| 搜索、导出等重接口 | 3–5 | Managed Challenge |
3. 建规则
控制台路径:Security → WAF → Rate limiting rules → Create rule。
匹配部分只能按路径写。想覆盖整个 API 前缀,用通配符:
starts_with(http.request.uri.path, "/api/")
或者用规则编辑器里的路径模式 /api/*。想排除某些路径,在表达式里加否定条件;免费档的表达式编辑器支持的运算符与自定义规则一致。
参数填三项:
- 请求数:上面算出来的阈值。
- 周期:10 秒(免费档唯一选项)。
- 缓解时长:命中后持续拦截多久。免费档可选范围比付费档小,以控制台下拉框为准;先选 10 秒到 1 分钟,观察误伤再调。
动作选择:Managed Challenge 优先。它不会给正常用户留下"被拒绝"的印象,只是多一次透明验证;对脚本是硬门槛。只有明确不该有高并发的接口(登录、短信发送、下单)才用 Block,并且配合自定义响应更友好。
4. 10 秒窗口的边界问题
固定窗口的计数在窗口切换的瞬间会归零,攻击者可以在边界两侧各打满一次,实际速率翻倍。这不是 Cloudflare 的缺陷,是所有固定窗口计数的共性。写了个本地脚本把这三种算法放在同一组请求上跑,输出如下:
const LIMIT = 5, WINDOW = 10_000; // 10 秒内最多 5 次
// 0-9 秒每秒 1 次;10-11 秒突发 8 次;20 秒后再来 3 次
requests total = 21 limit = 5 per 10 s
fixed-window allowed = 13 blocked = 8
sliding-window allowed = 9 blocked = 12
token-bucket allowed = 13 blocked = 8
boundary burst (9 reqs at 9.5s-10.4s): { allowed: 9, blocked: 0 } { allowed: 5, blocked: 4 }
最后一行是关键:9 次请求落在 9.5 秒到 10.4 秒之间,固定窗口把它们分到两个窗口,全部放行,实际是 0.9 秒内 9 次,超过了"10 秒 5 次"的意图;滑动窗口只放行 5 次。同一组流量,固定窗口多放了 4 次。
把这条结论用到配置上:既然免费档窗口固定是 10 秒,阈值就不要贴着业务上限填,往下压 30% 到 50%,用余量吸收边界突刺。真要精确控制,得在 Worker 里自己做计数(见下一条),或者用付费档的更长窗口把突刺摊平。
5. 在 Worker 里做应用层限流
WAF 的速率限制看不到业务身份(用户 ID、API Key),只认 IP。同一个办公室出口 IP 后面的多个用户会被算成一个,而用代理池的攻击者又能绕开 IP 维度。需要按用户维度限流时,在 Worker 里做:
// 简化示意:按 API Key 做 1 分钟固定窗口,计数放 KV 会撞写入限额,生产建议 Durable Objects
const key = request.headers.get("x-api-key") ?? "anonymous";
const bucket = `rl:${key}:${Math.floor(Date.now() / 60000)}`;
const used = Number(await env.RL.get(bucket) ?? 0);
if (used >= 60) {
return new Response("too many requests", { status: 429, headers: { "retry-after": "60" } });
}
await env.RL.put(bucket, String(used + 1), { expirationTtl: 120 });
这段代码有两个坑:KV 同一 key 每秒只能写一次,高频写入会丢计数;KV 的读写有延迟,计数不准。计数类场景用 Durable Objects 更合适,成本模型见 56-free-tier-limits-cheatsheet.md,完整项目见 100-project-api-proxy-ratelimit.md。
返回 429 时带上 Retry-After,比直接断开连接更容易让客户端做退避重试。
6. 确认规则真的触发
规则建完,到 Security → Events 里筛 Action 为 Rate limit 的事件。每条命中会记录触发的规则、IP、路径、计数。用循环请求一条测试路径就能看到:
for i in $(seq 1 30); do
curl -s -o /dev/null -w "%{http_code} " https://example.com/api/ping
done
预期输出是前面若干个 200,后面变成 403 或挑战页。如果全是 200,检查路径是否被缓存规则命中——缓存命中的请求不经过源站,但速率限制仍在边缘统计;真正会让规则失效的是灰云记录与 Worker 路由绕过。
常见问题
免费档能不能按 IP 段限制?
表达式里不能直接写 IP 段做计数维度,但可以在自定义规则里用 ip.src 拦掉整个网段,再让速率限制只管路径。两条规则配合,效果接近。
被限流的请求会计入 Workers 请求数吗?
会计入。速率限制在边缘判定,被拦下的请求不会到 Worker,但如果动作是 Managed Challenge 且用户通过了挑战,后续请求照常进入 Worker 并计费。
阈值调高之后攻击还在,怎么办?
速率限制对分布式低速攻击无效——每个 IP 只打几次,永远撞不到阈值。这类流量要靠 Bot 管理(79-bot-management.md)与人机验证(81-turnstile.md),或者上 WAF 自定义规则按特征拦。
小结
免费档的速率限制是一条"按路径、10 秒窗口、按 IP 计数"的规则。它的价值不在精确,而在于给最贵的接口加一道闸:登录、发短信、导出、下单。阈值按单用户 10 秒峰值乘安全系数算,往下压三成吸收边界突刺,动作优先用 Managed Challenge。
想按用户维度限流,得到 Worker 里自己写,并且避开 KV 的写入限额。判断是否该上更复杂的方案,看的是"被刷一次的代价"——如果一次刷能带来真金白银的损失,多写几十行代码是划算的。
数据来源与检索时间
检索时间 2026-11-10,官方页面用 HTTPS 直接抓取,返回 200。
| 来源 | URL | 对应数据 |
|---|---|---|
| 速率限制规则文档 | https://developers.cloudflare.com/waf/rate-limiting-rules/ | 各套餐规则条数(免费 1、Pro 2、Business 5、Enterprise 100)、免费档可用字段、周期上限、计数维度 |
| WAF 概览 | https://developers.cloudflare.com/waf/ | 速率限制在免费档为"one rule"、高级速率限制为付费附加 |
| Workers KV 限额 | https://developers.cloudflare.com/kv/platform/limits/ | 同一 key 每秒 1 次写入、每日写入额度(用于说明 KV 不适合做计数) |
核验说明
- 三种限流算法的对比为本机真实执行:Node v24.19.0 跑一段虚拟时钟脚本,请求序列固定,输出原样贴在文中,可复现。
- 官方文档为实际抓取,套餐表格取自页面里的 Availability 表。
- 未执行:本机无 Zone 权限,控制台建规则、查看 Security Events、用 curl 打真实阈值都没有实际做过;文中阈值表是按页面对免费档字段的限制推算的建议值。
- 未验证:免费档缓解时长的具体下拉选项未确认,文中只说"以控制台为准"。