Nginx 通过 location 与 limit_req 配合实现分层限流,支持路径嵌套、变量动态选 zone、if+error_page 条件降级三种方式;需注意 zone 不叠加、burst 独立、正则优先级及显式声明规则。

Nginx 的 location 块本身不直接支持限流,但可以通过与 limit_req 指令配合,在不同层级的 location 中嵌套或继承限流策略,实现“分层限流”——即按路径粒度、优先级或业务模块差异化控制请求速率。
基于路径层级嵌套限流
利用 location 的匹配顺序和作用域继承特性,可在父路径定义基础限流,子路径覆盖或叠加更严格的规则。Nginx 会优先匹配最具体的 location,其内部的 limit_req 生效,未定义则向上回溯(若上级有且未被子级显式禁用)。
示例:
limit_req_zone $binary_remote_addr zone=global:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=50r/s;
limit_req_zone $binary_remote_addr zone=admin:10m rate=5r/s;
<p>server {<br />
location / {<br />
limit_req zone=global burst=20 nodelay; # 全站兜底:10qps<br />
} </p><pre class='brush:php;toolbar:false;'>location /api/ {
limit_req zone=api burst=100 nodelay; # 接口层:50qps
}
location /api/admin/ {
limit_req zone=admin burst=10; # 后台接口:5qps,严格限制
} }
注意:/api/admin/ 匹配时,只生效其自身 limit_req,不会叠加执行 /api/ 和 / 的规则——Nginx 不支持“多 zone 同时生效”,必须靠精准匹配和 zone 分离来模拟分层。
用变量动态选择限流 zone
通过 map 指令构造上下文感知的限流键或 zone 名,让同一 location 内根据请求特征(如 URI 前缀、header、参数)切换限流策略,实现逻辑上的分层。
示例(按接口类型分流限流):
map $request_uri $req_zone {
~^/api/v1/user/ "user";
~^/api/v1/order/ "order";
~^/api/v1/report/ "report";
default "default";
}
<p>limit_req_zone $binary_remote_addr zone=user:10m rate=30r/s;<br />
limit_req_zone $binary_remote_addr zone=order:10m rate=100r/s;<br />
limit_req_zone $binary_remote_addr zone=report:10m rate=5r/s;<br />
limit_req_zone $binary_remote_addr zone=default:10m rate=20r/s; </p><p>server {<br />
location /api/ {<br />
limit_req zone=$req_zone burst=50 nodelay;<br />
}<br />
}这样,单个 location 就能根据 URI 自动路由到不同限流池,无需重复写多个 location 块,适合路径规则复杂或需频繁调整的场景。
结合 if + error_page 实现条件性限流降级
在关键 location 中,用 if 判断业务条件(如 header 标识、参数值),触发自定义限流行为,再通过 error_page 统一返回限流响应,形成“策略层 + 执行层”分离的分层结构。
示例(对未登录用户限流更严):
limit_req_zone $binary_remote_addr zone=logged:10m rate=60r/s;
limit_req_zone $binary_remote_addr zone=unlogged:10m rate=5r/s;
<p>server {<br />
location /api/ {<br />
set $limit_key $binary_remote_addr;<br />
if ($http_x_user_id = "") {<br />
set $limit_key "unlogged:$binary_remote_addr";<br />
} </p><pre class='brush:php;toolbar:false;'> # 动态 zone 名需提前定义,此处用固定 zone + key 区分
limit_req zone=logged burst=100 nodelay;
limit_req_status 429;
# 实际使用中常配合 error_page 拦截并重写响应
error_page 429 = @rate_limited;
}
location @rate_limited {
return 429 '{"error":"too many requests"}';
add_header Content-Type "application/json";
} }
该方式将“判断逻辑”和“限流执行”解耦,便于后期扩展风控维度(如 IP 地段、设备指纹等)。
避免常见分层陷阱
-
不要依赖 location 嵌套继承 limit_req:Nginx 不会自动合并父级和子级的限流指令,子
location必须显式声明自己的limit_req,否则沿用最近上级(可能不符合预期) - burst 缓存是 per-zone 的:不同 zone 的 burst 队列互不影响,不能靠多个 zone 累加缓冲能力
-
正则 location 优先级高于前缀匹配:若同时存在
location /api/和location ~ ^/api/v\d+/,后者会优先生效,需检查匹配顺序是否符合分层意图 -
limit_req 默认拒绝超出请求:如需排队等待,必须显式加
nodelay或设置delay参数,否则 burst 外的请求立即 503


















