
网站能打开,登录或调用接口却返回 429;换个网络又恢复正常。这通常与请求频率或配额有关,但返回这个状态码的可能是 CDN、WAF、Nginx,也可能是应用或第三方服务。直接扩大 Nginx 限额,未必能影响真正拒绝请求的那一层。
处理顺序应是确认谁返回了 429,再核对计数对象与实际请求量,最后调整策略或客户端行为。 不要先关闭所有防护,也不要用不断刷新或并发重试验证问题,这会延长受限时间并增加服务压力。
本文面向使用 CDN、反向代理与 Web 应用的站点维护人员。命令适用于常见 Linux 与 Nginx 环境,域名、路径和限额均为示例。托管平台、API 网关和商业 WAF 的规则语义需按各自文档确认,本文不假设所有产品采用相同计数方式。
429 表示什么,不表示什么
HTTP 429 表示客户端在一定时间内发送了过多请求。规范允许响应携带 Retry-After,但没有规定所有服务器必须按 IP 计数,也没有要求所有 429 都带该响应头。[1]
计数键可以是账号、API 密钥、会话、IP、接口或它们的组合。套餐配额、并发限制和上游 API 的策略也可能表现为 429,需要结合产品错误码与日志判断。它不能单独证明网站正在被攻击,也不能证明 CPU 或带宽已经耗尽。
Retry-After 可以是等待秒数,也可以是 HTTP 日期。[2] 客户端应按支持的格式解析,日期形式还需要可靠时钟。如果响应没有这个字段,应遵循接口约定采用有上限的退避,而非立即重试。
先保存一次真实失败请求
记录发生时间与时区、方法、URL 路径、账号或密钥标识、响应状态和请求 ID。不要记录完整令牌、Cookie 或敏感请求体。
对可安全重复的公开 GET 路径,可进行一次低频检查:
curl --connect-timeout 3 --max-time 10 -sS \
-D response-headers.txt -o response-body.txt \
https://example.com/status
示例的域名和路径须替换;登录、支付或写入接口不能直接照抄重放。响应文件也可能含敏感信息,应受控保存并及时清理。HEAD 请求可能走不同规则,因此不能用 curl -I 的成功替代原请求结果。
查看 Retry-After、产品错误码、请求 ID、响应正文与代理相关头部。Server 或某个响应头只能提供线索,可能被隐藏、改写或来自下游,不足以单独证明限流来源。
浏览器中还应检查是否存在前端循环请求、失败后立即重试、多个标签页同时轮询,或插件反复触发接口。一次页面访问可能产生许多 HTTP 请求,不能把“只有几个访客”直接等同于“请求量很低”。
沿 CDN、代理、应用的日志寻找同一请求
优先使用请求 ID 和时间关联各层日志。没有统一 ID 时,再结合方法、路径、来源和时间窗口,注意不同服务器的时钟与时区。
| 观察证据 | 更可能的来源 | 下一步 |
|---|---|---|
| 边缘安全日志显示命中频控,源站没有对应请求 | CDN、WAF 或边缘网关 | 检查规则 ID、计数键与作用范围 |
| Nginx 错误日志显示 limiting requests | Nginx 请求速率限制 | 核对 zone、location、rate 与 burst |
| 代理记录上游返回 429,应用日志有同一请求 | 应用或更下游服务 | 检查账号配额、应用中间件和上游错误 |
| 仅某个账号或密钥失败,换 IP 仍受限 | 账号或凭据维度限制 | 查看配额与调用来源,不要盲目换网络 |
“源站没有日志”还可能是日志采样、关闭记录或查询范围错误,不能据此直接定论。存在多层代理、重试和内部跳转时,状态记录会更复杂,应结合完整请求链路。
Nginx 已配置日志字段时,可观察 $status、$upstream_status 和 $limit_req_status。其中上游状态为 429 通常说明至少某个上游返回了该状态;没有上游状态也可能是本地处理或边缘拒绝,仍需其他日志佐证。日志中不要为了排查记录授权头或完整查询参数。
Nginx 的默认拒绝状态并不是 429
Nginx limit_req 使用漏桶方式控制请求速率。默认拒绝状态码是 503,只有配置 limit_req_status 429 等行为时,才会以 429 返回。[3] 因此,看到 429 不能自动认定是 Nginx 默认限流;看到 503 也不能排除限流。
以下片段说明配置位置与语义,不是推荐所有站点套用的阈值:
# 放在 http 上下文;zone 名称应避免与现有配置冲突
limit_req_zone $binary_remote_addr zone=login_demo:10m rate=5r/s;
# 放在对应 server 内,按实际接口选择匹配范围
location = /login {
limit_req zone=login_demo burst=10 nodelay;
limit_req_status 429;
proxy_pass http://app_backend;
}
该示例假定 app_backend 已定义,并应合并现有代理参数。rate 控制长期处理速率;burst 给短时间超额请求留下容量。nodelay 允许处于容量内的突发请求不经排队延迟,但没有取消限流,超出容量仍会被拒绝。[3]
5r/s 和 burst=10 只用于说明语法,不构成登录安全建议。正常请求分布、接口成本和安全风险不同,应根据日志制定阈值。不要把公共静态资源、登录接口和昂贵导出任务统一用同一策略。
配置修改前备份实际加载的文件,确认 location 匹配与继承关系。nginx -T 可辅助检查有效配置,但输出可能含内部地址或凭据,不能公开粘贴。确认语法后再按部署方式重载:
sudo nginx -t
sudo systemctl reload nginx
容器或其他进程管理方式需使用对应流程。重载失败时保留错误并恢复备份;不要通过重启整台服务器处理语法问题。有需要时可评估 limit_req_dry_run 观察候选策略,但它不是对现有防护的无条件替代,应限定范围并确认版本支持。[3]
接入 CDN 后,计数键可能把所有访客合在一起
如果源站看到的客户端地址都是代理节点,用 $binary_remote_addr 计数就可能把共享节点后的访客合并。这会表现为低流量访客也被拒绝,或某些地区集中受影响。
先对照日志中的连接来源与经过验证的真实访客地址,再检查 Real IP 配置。Nginx 的 realip 模块需要根据可信代理地址和指定头部恢复地址。[4] 不能直接信任任意客户端提供的 X-Forwarded-For,也不能把所有地址都加入可信代理范围。
正确方案应结合实际代理拓扑、供应商公布的代理网段、头部覆盖规则及源站访问控制。代理网段要维护更新;多跳代理还需确认可信链的解析规则。仅修改日志展示字段,没有改变限流使用的变量,可能仍然按旧键计数。
即使真实 IP 恢复正确,共享 NAT 下也有大量用户共用出口。登录与 API 访问可结合经验证的账号维度和业务规则,但匿名请求头不应直接成为可伪造的豁免依据。白名单同样应最小化、可审计,并设置复核时间。
应用和客户端的重试也需要调整
如果应用在调用第三方 API 时遇到 429,应核对具体配额:每秒请求、每分钟令牌、并发、账号日额度等含义可能不同。扩容网站服务器不能提高第三方账号额度,增加工作进程反而可能更快触发限制。
客户端处理可以包含:
- 优先遵循有效的
Retry-After和接口约定。 - 使用有随机抖动、等待上限和总次数上限的指数退避。
- 对重复读取做缓存或合并,限制并行任务数量。
- 对写入操作核对幂等性,避免恢复重试生成重复订单或通知。
没有统一退避秒数适合所有服务。队列积压时应按优先级和允许延迟处理,避免到期后一次性释放所有请求形成新峰值。不要轮换 IP、账号或密钥绕过服务条款与配额,也不要把 API 密钥嵌入公开前端以便调试。
调整后验证正常访问与防护是否同时成立
先用低频、正常业务请求验证误伤是否解除,再在授权的测试环境验证超额请求仍按预期受限。不要在生产环境直接跑高并发压测触发规则。
验收应覆盖受影响账号、真实网络入口、登录或关键接口,同时观察 429 比例、延迟、源站负载和下游配额。429 下降而数据库负载暴涨,说明只是把压力转移到了后端。
每次只修改明确相关的一项,记录原值、规则 ID、作用范围与回退步骤。若异常扩大,恢复已备份配置或原规则,并核对重载与生效状态。限流窗口内的旧计数可能继续影响结果,不能仅凭立即重试判断配置是否生效。
最终应能说明:谁返回了限制、按什么对象计数、正常请求为何命中、采取了什么修复。正常流量恢复且超额流量仍受控,才算完成处理。
参考资料
资料核验日期:2026 年 10 月 26 日。配置与命令为说明性示例,本文未声称已在读者生产环境实测。




