limit_req_status仅能修改限流状态码,无法跳转或返回内容;必须配合error_page = @name与internal命名location,用return内联响应实现零开销降级。

直接用 limit_req_status 无法实现“重定向至降级页”,它只负责改状态码;真要跳转页面或返回轻量兜底内容,必须配合 error_page + 内部命名 location 实现,且整个过程需保持低开销、零后端依赖。
明确 limit_req_status 的边界作用
limit_req_status 是一个纯状态码开关,仅影响响应头中的 Status 字段。它不生成响应体、不设置 Header、不触发跳转——哪怕你写 limit_req_status 302,也不会自动 Location 重定向。Nginx 不会帮你做 HTTP 重定向逻辑,它只是把 503 换成你指定的码(如 429、403、甚至 598)。
用 error_page 实现真正的“降级响应”
要返回静态 HTML 降级页(比如 /rate-limited.html),或直接输出轻量 JSON,必须走 error_page 内部跳转路径:
- 写法必须带等号:
error_page 429 = @degraded;(=表示内部重定向,客户端看到的仍是 429 状态) - 命名 location 要加
internal;,防止被外部直接访问 - 响应体用
return直接输出,避免proxy_pass引入延迟和失败点
示例配置:
limit_req_zone $binary_remote_addr zone=spider:10m rate=3r/s;
server {
location /api/ {
limit_req zone=spider burst=2 nodelay;
limit_req_status 429;
error_page 429 = @degraded;
}
location @degraded {
internal;
add_header Content-Type "text/html; charset=utf-8";
return 429 '<!DOCTYPE html>
<html><body style="margin:10% 20%;text-align:center">
<h2>请求过于频繁</h2>
<p>请稍后再试</p>
</body></html>';
}
}
为什么推荐返回内联 HTML 而非真实重定向?
所谓“重定向至降级页”,如果理解为 302 跳转到 /degrade.html,会带来额外问题:
- 多一次 HTTP 往返,增加延迟,违背“低消耗”目标
- 浏览器可能缓存 302,导致后续正常请求也被错误跳转
- 爬虫可绕过——它只认状态码和响应体,不执行 JS 或页面跳转
- 真实文件路径暴露服务结构,且需额外维护静态资源
而内联 HTML 响应:零 IO、零磁盘读取、零缓存干扰,Nginx 纯内存拼接返回,毫秒级完成。
适配爬虫识别与前端拦截的细节
若该限流同时面向真实用户(前端)和恶意爬虫,建议统一用 429 + 标准字段,方便前端拦截器识别并提示:
- 响应体固定含
"code": 429和"message",便于 Axios/Fetch 全局 catch - 添加
Retry-After: 60响应头(用add_header Retry-After 60;),让合规爬虫知道退避时长 - 避免在
@degraded中使用$binary_remote_addr等变量——它们在命名 location 中不可用;如需 IP 日志,改用map提前映射到全局变量


















