Nginx的limit_conn模块是对抗DDoS攻击中最实用、最轻量的手段之一,它通过限制每个客户端IP的并发连接数来防止服务器被海量请求压垮。具体做法是在Nginx配置文件的http块中定义一个共享内存区域,然后在server或location块中引用这个区域并设定最大连接数。比如设置limit_conn_zone $binary_remote_addr zone=addr:10m; 然后在location中写limit_conn addr 10; 就意味着每个IP最多只能同时建立10个连接,超出的直接返回503错误。这个方法不需要额外安装任何模块,Nginx原生自带,配置简单但效果显著,特别适合应对SYN Flood、HTTP Flood这类基于连接数耗尽资源的攻击。
为什么DDoS防护要从限制并发连接入手
DDoS攻击的核心逻辑就是用海量请求把你的服务器资源耗尽。带宽被打满、CPU被跑满、连接数被占满,这三种是最常见的攻击方式。其中连接数耗尽是最容易被忽视但危害极大的一种。一个普通的Web服务器,比如Apache默认的MaxClients可能也就256,Nginx虽然worker_connections可以设到几万,但如果攻击者用僵尸网络每个IP开几百个连接,几万个连接很快就被占满了。一旦连接数满了,正常用户根本访问不进来。limit_conn就是从源头上卡住每个IP能开的连接数,让攻击者没法用少量IP就把连接池撑爆。
limit_conn的核心原理和配置步骤
limit_conn模块依赖两个指令配合使用:limit_conn_zone和limit_conn。limit_conn_zone用来定义一个共享内存区域,用来存储每个IP的连接计数。这里有个关键点,必须用$binary_remote_addr作为key,而不是$remote_addr。因为$binary_remote_addr只占4个字节(IPv4)或16个字节(IPv6),而$remote_addr是字符串,占用空间大得多。10m的共享内存大约能存16万个IP的状态信息,对于大多数网站来说足够了。
http {
# 定义共享内存区域,key用binary_remote_addr,10m大约存16万个IP
limit_conn_zone $binary_remote_addr zone=addr:10m;
# 也可以按服务器域名限制
limit_conn_zone $server_name zone=perserver:10m;
server {
listen 80;
server_name example.com;
location / {
# 引用上面定义的addr区域,每个IP最多10个并发连接
limit_conn addr 10;
# 也可以同时限制每个域名的总连接数
limit_conn perserver 100;
}
}
}
limit_conn和limit_req的区别与配合使用
很多人容易把limit_conn和limit_req搞混。limit_conn限制的是并发连接数,也就是同时有多少个TCP连接建立在那里;limit_req限制的是请求频率,也就是单位时间内能发多少个请求。这两个解决的是不同层面的问题。DDoS攻击中,SYN Flood主要打的是连接层,用limit_conn防;HTTP Flood主要打的是请求层,用limit_req防。最佳实践是两个一起上,形成双重防护。
http {
# 限制连接数:每个IP最多10个并发
limit_conn_zone $binary_remote_addr zone=addr:10m;
# 限制请求频率:每个IP每秒最多5个请求
limit_req_zone $binary_remote_addr zone=req:10m rate=5r/s;
server {
location / {
limit_conn addr 10;
limit_req zone=req burst=10 nodelay;
}
}
}
上面这个配置的意思是:每个IP最多同时10个连接,每秒最多5个请求,但允许突发10个请求(burst=10),超过的会被延迟处理而不是直接拒绝(nodelay)。这种配置对正常用户几乎无感,但对攻击流量有很强的拦截效果。
如何根据业务场景调整limit_conn的数值
limit_conn设多少合适,这没有标准答案,得看你的业务类型。如果你是普通的企业官网、博客,每个用户正常浏览同时打开3-5个连接就够了,设5-10完全没问题。如果你是API服务、WebSocket应用,可能需要设高一些,比如20-50。如果你是下载站、视频站,用户可能会开很多连接做分片下载,那就得设到50甚至100。关键原则是:设一个正常用户不会触发、但攻击者会大量触发的阈值。
还有一个技巧,可以针对不同的location设置不同的limit_conn值。比如登录接口容易被暴力破解,可以设得很低,比如3-5;而静态资源页面可以设高一些。这样既保护了敏感接口,又不影响正常访问体验。
server {
location /api/login {
limit_conn addr 5;
}
location /static/ {
limit_conn addr 50;
}
location / {
limit_conn addr 10;
}
}
limit_conn在实际DDoS防护中的局限性
必须客观地说,limit_conn不是万能的。它只能防基于单IP大量连接的攻击,如果攻击者用的是分布式僵尸网络,每个僵尸IP只开几个连接,总量加起来还是很大,limit_conn就防不住了。这种情况需要配合其他手段,比如基于IP信誉的黑名单、基于行为分析的WAF、或者上游流量清洗服务。另外,limit_conn只能在Nginx这一层生效,如果攻击直接打到你的源站IP绕过了Nginx,那就完全没用了。所以它更适合作为整体防护体系中的一环,而不是唯一的防线。
还有一点,limit_conn是在Nginx的worker进程中统计的,如果你的Nginx只有一个worker,那所有连接都在一个进程里计数;如果有多个worker,每个worker独立计数,这意味着同一个IP的连接可能分散在不同worker上,实际能建立的连接数会比设定值乘以worker数。这在高并发场景下需要注意,可以通过调整worker_processes和limit_conn的值来平衡。
配合其他Nginx指令构建完整防护体系
单独用limit_conn效果有限,但如果和其他Nginx指令组合起来,防护能力会大幅提升。以下是几个推荐的组合策略:
第一,配合limit_rate限制带宽。攻击者不光开连接,还会大量下载消耗带宽。limit_rate可以限制每个连接的下载速度,比如设成50k/s,这样即使连接建立了,也不会把带宽吃光。
location /download/ {
limit_conn addr 5;
limit_rate 50k;
}
第二,配合proxy_timeout和keepalive_timeout缩短连接保持时间。攻击者喜欢长时间占着连接不释放,把超时时间设短一些,比如keepalive_timeout 15;,可以加快回收空闲连接,提高连接周转率。
第三,配合geo模块做IP段级别的限制。如果你发现某个IP段在攻击你,可以直接在geo里把整个段拉黑,比逐个IP限制高效得多。
geo $blocked {
default 0;
192.168.1.0/24 1;
10.0.0.0/8 1;
}
server {
if ($blocked) {
return 444;
}
}
监控和调优:让limit_conn真正发挥作用
配置好limit_conn之后不是就完事了,你得持续监控。Nginx的stub_status模块可以实时查看当前的连接数、活跃连接数等信息。建议开启这个模块,然后配合监控工具定时采集数据,观察在攻击发生时连接数的变化趋势。
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
通过监控你可以发现:如果正常流量下连接数就经常接近上限,说明你设的值太低了,需要调高;如果攻击时连接数远没到上限但服务器已经扛不住了,说明攻击方式不是连接数型的,需要换其他防护策略。这种基于数据的调优才能让limit_conn真正成为你防护体系中的有效组件。
总结:limit_conn是DDoS防护的基础但不是全部
Nginx的limit_conn是一个简单、高效、零成本的DDoS防护手段。它不需要额外硬件,不需要购买服务,几行配置就能上线。对于中小网站、个人项目、API服务来说,这是性价比最高的第一道防线。但任何单一手段都有天花板,limit_conn防得了单IP高并发,防不了分布式低并发;防得了连接层,防不了应用层。真正有效的DDoS防护一定是多层叠加的:网络层有流量清洗,传输层有连接限制,应用层有频率控制和行为分析。把limit_conn放在这个体系里,它就是一块坚实的基石。
最后提醒一点,配置limit_conn之后一定要在测试环境先验证,确认正常业务不受影响再上线。特别是WebSocket、长轮询、大文件下载这些场景,连接数需求和普通网页不一样,盲目设置可能会误伤正常用户。做好测试、做好监控、做好调优,limit_conn才能真正帮你挡住攻击。
