limit_conn_zone是Nginx中定义并发连接限制共享内存区的指令,必须置于http块内,以$binary_remote_addr为键可精准按真实IP限流,配合limit_conn方可实现全站单IP并发控制。

什么是 limit_conn_zone,它真能限制单IP全站并发吗
能,但有个关键前提:它只对连接数生效,不是请求数。用户开10个标签页、用HTTP/2复用连接,可能只占1个连接;而短连接频繁请求却会快速耗尽配额。Nginx不解析HTTP层的“用户身份”,$binary_remote_addr 是唯一可靠且内存友好的IP标识方式——它自动压缩IPv4为4字节、IPv6为16字节,比用 $remote_addr 节省约60%共享内存空间。
常见误用是写成 limit_conn_zone $remote_addr zone=perip:10m,这会导致IPv6地址被截断,后续匹配失败,看似限流无效。
- 必须用
$binary_remote_addr作为key - zone大小不能太小:10MB约支持8万IP(按平均32字节/key估算),高流量站点建议从20m起步
- 该指令只能出现在
http块,不能放在server或location内
limit_conn 放在哪个位置才真正作用于“全站”
放在 http 块最外层,才能覆盖所有 server 和 location。若只放在某个 server 块里,就只对该域名生效;若放在 location /api/,那静态资源和首页完全不受控。
注意:一个连接对应一个TCP连接,无论这个连接上发了多少个HTTP请求(尤其在keepalive开启时)。所以实际并发感知比想象中“宽松”。
- 正确写法:
http { limit_conn_zone $binary_remote_addr zone=perip:20m; limit_conn perip 10; ... } - 错误写法:
limit_conn perip 10单独写在location里——它不会继承http块的定义,会报错“unknown limit_conn zone” - 若需差异化策略(如API更严、静态资源放宽),得用多个
limit_conn_zone+ 多个limit_conn组合,不能靠条件判断
为什么加了配置却没看到503,或者日志里全是“limiting connections”却没生效
典型原因是没有启用 limit_conn_log_level 和 limit_conn_status,导致行为不可见。默认情况下,超限时Nginx只记一条error日志,不返回503,客户端甚至感知不到被限——它只是被挂起,直到有连接释放。
更隐蔽的问题是:如果后端是长连接代理(如gRPC或WebSocket),limit_conn 会把整个连接计为1,但业务逻辑可能允许它持续占用几十秒甚至几分钟,造成“假性拥堵”。
- 务必显式设置:
limit_conn_log_level warn;和limit_conn_status 503; - 检查access日志是否包含
$limit变量(需编译时含--with-http_realip_module才能准确识别真实IP) - 用
ss -tn | grep :80 | wc -l配合ab -n 100 -c 50 http://your.site/实测,比看日志更快验证
与 limit_req 混用时要注意什么
limit_conn 和 limit_req 解决的是不同维度问题:前者管“同时有几个TCP连接”,后者管“每秒最多几个请求”。两者可共存,但顺序和作用域必须理清。
例如:你设了 limit_conn perip 5 和 limit_req zone=burst burst=20 nodelay,那么一个IP最多维持5个连接,每个连接内每秒最多处理4个请求(20÷5)。但如果用户用curl反复新建连接,limit_conn 会先拦住第6个连接,limit_req 根本没机会触发。
- 不要在同一个
location里对同一zone既做limit_conn又做limit_req,容易互相干扰 - 如果用了
proxy_pass,确保limit_conn在proxy_pass之前执行——它基于Nginx接收连接的时刻判断,不是后端响应时刻 - HTTP/2下,单连接多路复用会使
limit_conn显得“过于宽泛”,此时应优先考虑limit_req+burst控制QPS
真正难的是平衡:设太松挡不住爬虫,设太紧误伤移动端弱网用户。上线前一定要用真实网络环境(比如4G模拟)跑一轮压测,而不是只看本地 ab 结果。


















