直接用limit_conn模块可按真实客户端ip限连,核心是定义共享内存区、启用限制并还原真实ip;需用$binary_remote_addr建zone,server/location中配置limit_conn,代理环境须用realip_module还原ip。

直接用 limit_conn 模块就能实现,核心是按真实客户端 IP 建立连接计数器,超出即拒绝新连接,不消耗后端资源。
第一步:在 http 块中定义共享内存区
必须写在 http{} 里,不能放在 server 或 location 内:
- 推荐使用
$binary_remote_addr作为 key,比$remote_addr更省内存(IPv4 固定 4 字节) -
zone=perip:10m表示创建名为perip的共享内存区,大小 10MB,约支持 16 万~32 万个独立 IP - 如果日志出现
limit_conn: cannot add connection,说明内存不足,需增大 size(如改20m) - zone 名称必须全局唯一,后续
limit_conn指令靠它匹配
示例:
http {limit_conn_zone $binary_remote_addr zone=perip:10m;
}
第二步:在 server 或 location 中启用限制
limit_conn 可出现在 http、server、location 任意一级,但实际生效值取嵌套层级中最内层的最小数值:
- 整站限制:在
server块中加limit_conn perip 10 - 仅限高负载路径(如下载、API、WebSocket):写在
location /api/v1/download/ { limit_conn perip 3; } - 避免放在
location /下,否则静态资源也受限制,反而增加 CPU 开销 - HTTP/2 下一个 TCP 连接可承载多个 stream,
limit_conn仍按连接计,不是按 stream 计
示例(仅限下载接口):
location ^~ /api/v1/download/ {limit_conn perip 3;
limit_conn_status 429;
limit_conn_log_level error;
proxy_pass http://backend;
}
第三步:代理环境下还原真实 IP
有 CDN、SLB 或 Nginx 前置代理时,默认 $binary_remote_addr 是上一跳代理地址(如 10.0.0.1),会导致所有用户被当成同一个 IP 限流或完全失效:
- 确认已加载
http_realip_module(主流发行版默认包含) - 在
http或server块中配置可信上游:set_real_ip_from 192.168.1.0/24;(填你实际的代理网段) - 指定 header:
real_ip_header X-Forwarded-For;或X-Real-IP(根据实际代理设置) - 更稳妥的做法是改用
$realip_remote_addr作为limit_conn_zone的 key
增强可观测性与防护效果
让限流更可控、问题更易排查:
-
limit_conn_status 429;:返回429 Too Many Requests,语义比默认503更明确 -
limit_conn_log_level error;:只在真超限时记 error 日志,避免刷屏 - 搭配
limit_rate 200k;防止单连接持续占带宽 - 设置
keepalive_timeout 15;及时释放空闲长连接,避免无效占用配额











