用户反馈"页面突然打不开了,提示 429",或者你自己的监控发现接口大面积返回 429 Too Many Requests。这个状态码的意思是:你在单位时间内请求得太频繁,被服务器或中间网关限流了。429 本身不是故障,它是限流机制在正常工作——问题往往在于"限制得太严,误伤了正常用户",或者"有人真在刷接口,需要区分对待"。
要处理 429,得先搞清楚是谁在限流:Nginx 自己的 limit_req/limit_conn、云 WAF、API 网关,还是应用代码里的限流器。不同层的触发原因和调整方式完全不同。这篇文章讲清楚 429 的触发机制、Nginx 限流配置怎么写、云 WAF 和 API 网关怎么排查、Retry-After 头的作用,以及怎么设置才不至于误伤正常访问。
一、429 是什么,什么时候会触发
429 由 RFC 6585 定义,表示"请求过多",通常伴随一个 Retry-After 响应头,告诉客户端"过多少秒后再试"。触发 429 的典型场景:
- 接口防刷:限制单个 IP 或单个用户在一段时间内的请求次数。
- 防 CC 攻击:源站或 WAF 对高频请求做清洗。
- 保护后端:后端处理能力有限,用限流把超过能力的请求挡在门外。
- 第三方 API 配额:调用外部接口超出了配额,对方返回 429。
区分点是"限制的粒度":按 IP、按用户、按接口、还是按 API Key。定位 429 要先知道被限的是哪个维度,才能判断是否合理。
一个关键的排查思路:看触发 429 的请求有什么共同点。是不是都来自同一个 IP、同一个 UA、同一个接口?正常用户分布的请求被限,说明阈值太低;集中在少数来源,说明可能是攻击或爬虫。
二、用访问日志确认是不是限流
先从日志确认 429 的范围和来源。
# 统计 429 请求的数量与路径
sudo awk '$9 == 429 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
# 看 429 都是哪些 IP 触发的(判断是否集中在少数来源)
sudo awk '$9 == 429 {print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
# 看这些请求的 UA,区分浏览器和脚本
sudo awk '$9 == 429' /var/log/nginx/access.log | tail -20
如果 429 集中在少数 IP 且 UA 是脚本特征,多半是爬虫或攻击被正常限流挡下;如果 429 分布在大量不同 IP、UA 是正常浏览器,多半是阈值设置过严,误伤了真实用户。Nginx 的限流日志在 error.log 里会有 limiting requests 或 limiting connections 的关键字:
sudo grep -i -E 'limiting (requests|connections)' /var/log/nginx/error.log | tail -20
三、Nginx 的 limit_req 与 limit_conn
Nginx 有两套限流指令:limit_req 按请求速率限流,limit_conn 按并发连接数限流。
limit_req 的核心是先定义一个共享内存区(zone),记录每个 key 的请求速率,再在 location 里引用。
# http 段:定义限流区
# 语法:limit_req_zone $key zone=名称:大小 rate=速率;
limit_req_zone $binary_remote_addr zone=req_per_ip:10m rate=10r/s;
server {
location /api/ {
# 引用限流区,burst 允许突发的额外请求数,nodelay 表示突发请求立即处理不排队
limit_req zone=req_per_ip burst=20 nodelay;
limit_req_status 429; # 被限时返回的状态码,默认 503
proxy_pass http://backend;
}
}
几个参数要理解透:
| 参数 | 含义 | 建议 |
|---|---|---|
$binary_remote_addr |
以客户端 IP 为限流 key(二进制形式更省内存) | 按 IP 限流常用 |
zone=名称:大小 |
共享内存区,10m 大约能存 16 万个 IP 状态 | 视 IP 数量调整 |
rate=10r/s |
平均速率,每秒 10 个请求 | 按接口能力设 |
burst=20 |
允许瞬时突发额外 20 个请求排队 | 给正常用户留缓冲 |
nodelay |
突发请求立即处理,不按速率排队 | 体验更好,但更耗后端 |
limit_req_status |
触发限流时返回的状态码 | 设 429 更规范 |
这里有个常见误区:不加 burst,只要超过 rate 就立刻 429,正常用户打开一个页面并发几个请求就可能被误伤。加上合理的 burst 和 nodelay,能容忍正常突发,只在真正高频时才拦。
如果按用户维度限流(比如按登录用户的 ID),key 可以换成变量:
# 示例:优先按用户标识限流,访问受限时以 cookie 或 header 里的用户标识为 key
limit_req_zone $http_x_user_id zone=req_per_user:10m rate=20r/s;
limit_conn 限制单个 key 的并发连接数,防止一个来源占满连接:
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
server {
location /download/ {
limit_conn conn_per_ip 10; # 单 IP 最多 10 个并发连接
limit_conn_status 429;
}
}
配置完成后测试并重载:
sudo nginx -t && sudo systemctl reload nginx
四、云 WAF、API 网关与应用层限流
如果 Nginx 日志里没有 limiting 关键字,但用户确实收到 429,那限流可能发生在更上层或更深层。
云 WAF / CDN:很多云厂商的 WAF 提供 CC 防护和频率限制,触发后返回 429 或自定义拦截页。这类限流在服务器本地日志里看不到,必须到云控制台查看"防护日志""拦截事件"。常见触发条件包括:单 IP 高频访问、命中恶意特征、地域限制。
API 网关:如果请求经过 API 网关(如 Nginx 之外的网关服务、Kong、企业自研网关),网关层通常有独立的限流策略,按 API Key、路由、消费者维度配置,返回 429 时也会带 Retry-After。排查要去网关的管理界面看限流命中记录。
应用层限流:后端代码里也可能有限流器(比如令牌桶、漏桶、滑动窗口算法)。这类限流在应用日志里记录,报错往往伴随业务上下文。例如:
# 应用日志里搜索限流相关关键字
sudo grep -iE 'rate limit|too many|throttl' /var/log/app/app.log | tail
定位顺序建议:先看是不是 Nginx,再看是不是 WAF/网关,最后看应用层。前提是能拿到各层的日志或控制台访问权限。
五、Retry-After 头与友好处理
规范的 429 响应应该带上 Retry-After,告诉客户端多久后可以重试。这对 API 客户端和爬虫很重要——没有它,客户端可能立刻重试,反而加重限流压力。
# Nginx 触发限流时可以附加 Retry-After
location /api/ {
limit_req zone=req_per_ip burst=20 nodelay;
limit_req_status 429;
add_header Retry-After 5 always;
}
对外提供服务时,还要考虑用户体验:直接返回裸 429 会让用户困惑,可以配一个友好的错误页,说明"访问过于频繁,请稍后再试"。
# 自定义 429 页面(可选)
error_page 429 /429.html;
location = /429.html {
root /var/www/errors;
internal;
}
对于搜索引擎爬虫,如果被 429 挡住,会降低抓取频率,长期可能影响收录。所以对爬虫应尽量放行或有独立的宽松策略,别让防刷规则误伤(这一点和第 067 篇呼应)。
六、怎么设置才不误伤正常用户
限流是"防坏人"和"别挡好人"之间的平衡。几个实用原则:
- 阈值要有数据支撑。先统计正常用户的峰值请求速率(看访问日志的 P95/P99),把阈值设在峰值之上留出余量,而不是拍脑袋定个很低的值。
- 善用 burst。给正常突发留缓冲,避免页面并发加载被误伤。
- 区分维度和粒度。静态资源、接口、上传等可以有不同策略;登录用户和匿名 IP 分开限。
- 白名单。公司出口 IP、监控系统、搜索引擎爬虫、合作方接口加白名单,避免误伤。
- 可观测。对 429 数量做监控和告警,一旦异常升高立刻能发现是攻击还是配置问题。
# 白名单示例:监控和搜索引擎爬虫不限流(通过 map 生成限流 key)
map $http_user_agent $limit_key {
default $binary_remote_addr;
~*Googlebot ""; # 空 key 表示不限流
~*bingbot "";
"" $binary_remote_addr;
}
limit_req_zone $limit_key zone=req_dynamic:10m rate=10r/s;
# 公司出口 IP 白名单
geo $limit_ip {
default 1;
203.0.113.0/24 0; # 0 表示不限流
}
风险提示:白名单里的 IP 段要核对准确,把不受限的 IP 段写得过宽会被滥用;限流阈值调低或临时加严都可能误伤真实用户,改动前记录原值、低峰期操作并持续观察 429 比例。
用数据反推合理阈值
阈值不该靠猜。做法是先统计正常流量的实际速率,再把阈值定在正常峰值之上。可以从访问日志按秒级或分钟级聚合每个 IP 的请求数,找出正常用户的最高值,再留出两到三倍余量。
# 统计单个 IP 每分钟的请求峰值分布(辅助定阈值)
sudo awk '{print $1, substr($4,2,17)}' /var/log/nginx/access.log \
| sort | uniq -c | sort -rn | head -20
如果发现正常用户峰值大约每分钟 30 次,那么 rate=10r/s(约每分钟 600 次)配 burst=20 就相当宽松,不会误伤;反过来,如果阈值定成 rate=1r/s 又没给 burst,正常的页面加载和接口并发就会被打成 429。合理的做法是"正常用户感觉不到限流,异常高频立刻被拦"。同时要把阈值和告警挂钩:如果 429 数量突然飙升,先判断是攻击还是配置问题,再决定是调高阈值还是加强封禁。
常见问题
Q:429 和 503 有什么区别?
503 是"服务暂时不可用"(过载或维护),429 更强调"你请求太多了"。Nginx 限流默认返回 503,建议用 limit_req_status 429 改成更准确的 429。
Q:Nginx 日志里没有 limiting 关键字,但用户收到 429?
说明限流不在 Nginx。查云 WAF/CDN 的拦截日志、API 网关的限流记录,或应用层限流器的日志。
Q:怎么区分是被攻击还是阈值太低?
看 429 的来源分布。集中在少数 IP/UA 是攻击或被刷;分散在大量正常 UA 是阈值过严,需要提高 rate 或加大 burst。
Q:加了 Retry-After 有什么用?
告诉客户端多久后重试,避免客户端立刻重试导致雪崩。对 API 客户端和爬虫尤其重要。
Q:限流会不会影响搜索引擎收录?
会。爬虫被 429 挡住会降低抓取频率。建议对正规爬虫放行或有独立宽松策略,并监控爬虫拿到的 429 数量。
总结
429 表示请求频率超限,是限流机制在工作,处理重点是分清"阈值过严误伤"还是"有人真在刷"。
排查从访问日志入手,看 429 集中在哪些 IP、路径、UA,再看 Nginx error.log 有没有 limiting 关键字。
Nginx 用 limit_req(按速率)和 limit_conn(按并发)限流,limit_req_zone 定义共享区,务必配合理的 burst 避免误伤,并把状态码设成 429。
如果 Nginx 没限流,往上看云 WAF/CDN 和 API 网关,往里看应用层限流器,逐层定位。
配置要用真实数据支撑阈值、给白名单放行监控和爬虫、带 Retry-After 头、对 429 做监控告警,改动前备份并在低峰期调整。
企业QQ咨询




