limit_req_zone必须且只能定义在http块内,用于声明共享内存区域以实现限流,若误置于server或location块中将导致nginx启动失败并报错“directive is not allowed here”。

limit_req_zone 必须在 http 块里定义,不能放 location 里
直接把 limit_req_zone 写进 location 或 server 块会报错:nginx: [emerg] "limit_req_zone" directive is not allowed here。它只能出现在 http 上下文,因为要初始化共享内存区,供所有 worker 进程共用。
常见错误写法:
server {
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s; ← 错!
location /api/ {
limit_req zone=api burst=20;
}
}
正确做法是:
- 用
$binary_remote_addr(不是$remote_addr),省内存、查得快 - size 要够:10m ≈ 存 16 万个 IP 状态;若业务 IP 量大,升到 20m 或 30m
- rate 设值别拍脑袋:先看监控里接口日常 P95 QPS,再留 20%~30% 余量
- 如果走 CDN 或 LB,
$binary_remote_addr会变成代理 IP,得换$http_x_forwarded_for并配set_real_ip_from
burst 参数不是“越大越好”,超了等于没限流
burst 是漏桶的容量,不是并发上限。设成 burst=1000 + nodelay,相当于把后端压测没过的那波流量全放过去了,只是失败时间从第 1 秒延后到第 10 秒——内存耗尽、连接打满、后端 OOM 才是真问题。
合理取值要看接口类型:
- 读接口(如 /product/detail):日常 QPS 80 →
burst=120(≈ 1.5 秒缓冲) - 写接口(如 /order/create):慎用排队,优先
burst=10 nodelay,但必须确认后端能扛住这 10 笔并发(比如 DB 连接池 ≥ 20、事务执行 - 健康检查或静态资源:直接
burst=0,避免干扰探针响应或缓存命中率 - 误配典型症状:响应时间分布突然变宽(如 100ms/300ms/800ms/2s 都有),说明漏桶节奏被破坏
不加 nodelay 时,burst 请求会排队,但延迟不可控
默认行为是“匀速放行”:假设 rate=5r/s、burst=10,第 6~15 个请求会被塞进队列,按每 200ms 放一个。但这个延迟不体现在响应头里,客户端感知就是卡顿,且无法区分是网络抖动还是限流排队。
想让排队可观察、可调试,得配合两个动作:
- 加
limit_req_log_level warn:否则只记录 error(即 503),看不到排队动作 - 在 log_format 里加变量:
$limit(是否触发限流)、$limit_key(哪个 key 触发)、$limit_rate(当前速率) - 压测验证:用
wrk -t4 -c100 -d10s http://x/api,看响应时间是否呈固定间隔(如 200ms、400ms、600ms…),才是漏桶生效
自定义限流返回码和页面,别只依赖 503
默认返回 503 对前端不友好,尤其移动端容易误判为服务宕机。用 limit_req_status 可改成业务能识别的状态码,再配 error_page 返回结构化响应。
例如:
limit_req_status 429;
error_page 429 /rate_limited.json;
location = /rate_limited.json {
default_type application/json;
return 429 '{"code":429,"msg":"请求太频繁,请稍后再试"}';
}
注意点:
-
limit_req_status必须写在limit_req同一作用域(location 或 server),不能只放在 http 块 - 返回 JSON 时,
default_type要显式设对,否则可能被浏览器当 text/html 解析 - 如果用了
nodelay,429 只会在 burst 溢出时触发;没加 nodelay 时,burst 内请求不会 429,只是慢











