429 Too Many Requests
用法
429 Too Many Requests 状态码表示 HTTP 请求数量已超过允许的阈值。例如当服务器想要限制每小时的 API 调用次数时就会发生这种情况。
规范未定义服务器如何统计 HTTP 请求或识别用户。速率限制可以按资源、按用户、跨整个服务器或跨多个服务器进行。用户识别通过凭据完成,或者也可以使用 Cookie。
服务器可以选择包含一个 Retry-After HTTP 头,以告知客户端何时可以再次接受请求。
注意
速率限制(rate limiting)对时间窗口内的请求总数设置上限(硬性截断),而限流(throttling)则通过排队或延迟响应来减慢请求处理。等待时长取决于 Retry-After 头。若没有 Retry-After,常见的时间窗口从 60 秒到 24 小时不等,视 API 而定。VPN 用户会与许多其他用户共享 IP 地址,因此按 IP 的速率限制会影响同一服务器上所有的 VPN 用户。
SEO 影响
Googlebot 将 429 视为服务器错误信号。当站点返回 429 响应时,Google 会暂时降低整个站点的抓取速率。一旦恢复正常响应,抓取速率会逐渐回升。与其他 4xx 状态码不同,429 响应会影响抓取预算(crawl budget)。已被索引的 URL 上持续返回 429 会导致其从搜索结果中移除。要让 Googlebot 减速,429 是正确的状态码。401 和 403 不会降低抓取速率。Bingbot 同样会在 429 响应时降低其抓取速率。此外,Bing 还支持在 robots.txt 中使用 crawl-delay,作为基于状态码限流的替代方案。
示例
客户端请求某个资源,服务器返回 429 Too Many Requests。
请求
GET /current-news HTTP/1.1
Host: example.com
响应
HTTP/1.1 429 Too Many Requests
Retry-After: 1800
Content-Type: text/html
Content-Length: 173
<html>
<head>
<title>Request Limit Exceeded</title>
</head>
<body>
<p>The request limit has been reached.
Try again in 30 minutes.</p>
</body>
</html>
如何修复
检查响应中的 Retry-After 头。其值要么是秒数,要么是一个 HTTP 日期,表示服务器何时再次接受请求。在指示的时间之前暂停所有请求。
在重试尝试之间实现带抖动(jitter)的指数退避。每次失败后将等待时间翻倍(1s、2s、4s、8s),并加入一个随机偏移,以防止多个客户端在同一时刻重试而制造新的流量峰值。
在触及上限之前监控速率限制头。许多 API 会包含 X-RateLimit-Limit、X-RateLimit-Remaining 和 X-RateLimit-Reset 头。跟踪剩余计数并主动放慢请求。
标准化的速率限制字段
除了事实标准的 X-RateLimit-* 头之外,IETF 正在将 RateLimit 和 RateLimit-Policy 响应字段标准化,用于配额和重置信号。
在客户端对外发请求排队。带有受控派发速率的请求队列可防止突发流量超出限制。当端点支持批量操作时,将多个操作合并为单次 API 调用。
对于在请求之间预期不会变化的数据,缓存响应。重复的相同调用是不必要地消耗速率限制的最常见来源。
在服务器端的 nginx 中,limit_req_zone 和 limit_req 指令控制按 IP 或按端点的速率限制。设置 limit_req_status 429; 使被限流的请求返回正确的状态码,而不是默认的 503。使用 burst 和 nodelay 参数在不立即拒绝客户端的情况下允许短暂的流量峰值。
对于 AWS API Gateway,在阶段(stage)或方法级别配置限流设置。当默认阈值过于严格时,提高速率和突发容量以匹配预期的流量模式。
代码参考
.NET
HttpStatusCode.TooManyRequests
Rust
http::StatusCode::TOO_MANY_REQUESTS
Rails
:too_many_requests
Go
http.StatusTooManyRequests
Symfony
Response::HTTP_TOO_MANY_REQUESTS
Python3.5+
http.HTTPStatus.TOO_MANY_REQUESTS
Apache HttpComponents Core
org.apache.hc.core5.http.HttpStatus.SC_TOO_MANY_REQUESTS
Angular
@angular/common/http/HttpStatusCode.TooManyRequests