必须使用 $binary_remote_addr 而非 $remote_addr 进行限流,因后者字符串长度不固定导致哈希不均、限流失效;前者 IPv4 固定 4 字节、IPv6 固定 16 字节,哈希稳定,是 Nginx 官方唯一推荐方案。

直接用 $remote_addr 限制单 IP 并发请求是不推荐且容易失效的做法,尤其在混合 IPv4/IPv6 环境下。它不能准确区分不同 IP,会导致限流失效或误伤。
真正安全、可靠的方案必须基于 $binary_remote_addr ——这是 Nginx 官方唯一明确推荐用于限流的 IP 表示方式。
为什么不能用 $remote_addr
-
$remote_addr是字符串格式:IPv4 如"192.168.1.100"(13 字符),IPv6 如"2001:db8::1"(12 字符),长度不固定; - Nginx 内部对 key 做哈希时,字符串长度差异会导致哈希分布严重不均;
- 结果是多个不同 IP 被哈希到同一个桶里,有的完全不受限,有的被连带封禁;
- 官方文档和多个生产实践反复验证:
$remote_addr在limit_req_zone或limit_conn_zone中不可靠。
正确做法:用 $binary_remote_addr 控制并发请求频率
✅ 第一步:定义请求限流区域(http 块内)
http {
limit_req_zone $binary_remote_addr zone=ip_freq:10m rate=5r/s;
}-
zone=ip_freq:10m:分配 10MB 共享内存,约可存 16 万个活跃 IP; -
rate=5r/s:每秒最多 5 个请求(令牌桶速率); - key 必须是
$binary_remote_addr,IPv4 固定 4 字节,IPv6 固定 16 字节,哈希稳定。
✅ 第二步:在 location 中启用限流
location /api/ {
limit_req zone=ip_freq burst=10;
proxy_pass http://backend;
}-
burst=10:允许最多 10 个请求排队等待(非并发,是缓冲槽位); - 不加
nodelay,请求超速时会延迟处理,避免突发打爆后端; - 若想硬拦截(超了直接 503),改用
burst=0,并配limit_req_status 503。
如何只限制特定 IP(比如黑名单)
Nginx 不支持 if 套 limit_req,必须用 map 构建动态 key:
http {
map $binary_remote_addr $limit_key {
default $binary_remote_addr;
192.168.1.200 ""; # 白名单,空 key 不计数
2001:db8::ff00 ""; # 同样跳过
}
limit_req_zone $limit_key zone=blacklist:10m rate=2r/s;
}
server {
location / {
limit_req zone=blacklist burst=3;
}
}- 白名单 IP 的
$limit_key为空字符串,limit_req_zone会忽略空 key; - 黑名单或目标 IP 则正常进入限流逻辑;
-
map必须写在http块顶层,不能放在server或location里。
进阶:防爬虫还要控连接数?加上 limit_conn
请求频率控制(limit_req)管的是“单位时间请求数”,
而并发连接控制(limit_conn)管的是“当前 TCP 连接数”,两者互补:
http {
limit_conn_zone $binary_remote_addr zone=perip:10m;
}
server {
location /download/ {
limit_conn perip 3; # 每 IP 最多 3 个并发连接
limit_req zone=ip_freq burst=5;
limit_conn_status 429;
limit_req_status 429;
}
}-
limit_conn perip 3对抗慢速攻击、长连接爬虫非常有效; - 返回
429 Too Many Requests比默认503更语义清晰; - 若前端有 CDN,需配合
real_ip_module+set_real_ip_from提取真实 IP。
小结关键点
- 绝对不要在
limit_req_zone或limit_conn_zone中使用$remote_addr; - 所有限流 key 都应优先选
$binary_remote_addr; -
burst不是并发上限,是排队缓冲区大小;要控并发请用limit_conn; - 条件限流靠
map,不是if; - 代理环境下记得启用
real_ip_module并配置可信源,否则限的全是 CDN 节点 IP。
不复杂但容易忽略。


















