基于Nginx的防CC攻击配置教程:限流与黑白名单实战
摘要:# 别让CC攻击拖垮你的网站:一个Nginx老手的实战配置手记 我前两天刚处理完一个客户的紧急工单,那场面,真够呛的。一个做电商促销的站点,活动页面突然就卡死了,后台一看,全是来自一堆陌生IP的、高频的、针对某个特定商品页的请求。服务器CPU直接飙满,数…
别让CC攻击拖垮你的网站:一个Nginx老手的实战配置手记
我前两天刚处理完一个客户的紧急工单,那场面,真够呛的。一个做电商促销的站点,活动页面突然就卡死了,后台一看,全是来自一堆陌生IP的、高频的、针对某个特定商品页的请求。服务器CPU直接飙满,数据库连接池耗尽,正常用户根本刷不出来。说白了,这就是典型的CC攻击(Challenge Collapsar),不搞垮你服务器,就专门恶心你,让你业务中断。
很多刚接触这行的朋友,一听说要防CC,第一反应就是“上高防”、“买WAF”。当然,有钱上高防IP、高防CDN肯定是省心,但说实话,对于很多中小项目、或者预算有限的团队来说,第一道真正靠谱的防线,往往就是你手头那台Nginx。
今天咱不聊那些PPT上吹得天花乱坠的“全自动智能清洗”,就扎扎实实地,把我自己常用的、基于Nginx的防CC配置掰开揉碎了讲给你听。这方案不敢说能扛住国家级别的攻击,但对付市面上90%的瞎搞、爬虫乱刷、恶意竞争,真够用了。而且,配置就在你手里,效果立竿见影。
核心就两板斧:限流与黑白名单
防CC,本质上是在和“频率”与“身份”作斗争。限流,管的是“频率”,甭管你是谁,每秒请求太多我就掐掉。黑白名单,管的是“身份”,是朋友就放行,是恶客就坚决拦在门外。这两招配合起来,效果1+1>2。
第一板斧:用limit_req模块给请求“上笼头”
Nginx自带的limit_req_zone和limit_req指令,是我们做限流最核心的工具。它的原理叫“漏桶算法”,你可以想象成在水龙头下面放个桶,水(请求)可以任意速率流进来,但只能以固定的速率流出去。超出的部分,要么排队等着(延迟处理),要么直接溢出去(拒绝)。
来,看配置。我们一般先在Nginx的http区块里,定义一个共享内存区来存放限流状态:
http {
# 定义一个名为“perip”的限流规则,以客户端IP作为键值。
# 分配10MB的内存空间来存储状态,平均速率限制为每秒10个请求(rate=10r/s)。
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
# 再定义一个针对服务器全局的限流区,防止总请求数过高(可选)
limit_req_zone $server_name zone=perserver:10m rate=100r/s;
... 其他http配置 ...
}
我解释一下几个关键点:
$binary_remote_addr:这是用客户端的IP地址。用二进制格式存,比字符串省空间。这是最常用、也最有效的维度,因为大多数CC攻击来自分散的IP。zone=perip:10m:定义了一个叫“perip”的共享内存区,大小10MB。1MB大概能存1.6万个IP的状态,10MB对付一般情况绰绰有余了。rate=10r/s:核心参数!意思是每个IP每秒最多只允许通过10个请求。这个数你得自己掂量,动态页面(像PHP、Node.js渲染的)可以设低点(比如5-10),静态资源(图片、CSS)可以设高点(比如50)。设太低可能误伤正常用户,太高又没防护效果。
定义好了规则,怎么用呢?在具体的server或location区块里加上:
server {
location / {
# 应用名为“perip”的限流规则
# burst参数是“桶”的容量,允许突发超过rate的请求先排队,这里设5。
# nodelay参数表示,如果请求数在burst范围内,则立即处理,无需排队等待。
limit_req zone=perip burst=5 nodelay;
# 如果需要,也可以应用全局限流
# limit_req zone=perserver burst=20 nodelay;
... 你的代理配置或root目录 ...
}
# 对于静态文件,可以放宽限制或者干脆不限
location ~* \.(jpg|jpeg|gif|png|css|js|ico)$ {
# 静态资源,可以放宽到每秒50次,突发20个
limit_req zone=perip burst=20 nodelay;
expires 30d;
access_log off; # 顺便关个日志,减轻磁盘压力
}
# 特别关键的登录或提交接口,可以限制得更死
location /api/login {
limit_req zone=perip burst=3 nodelay;
... 代理到后端 ...
}
}
这里有个大坑我得提醒你:burst和nodelay的配合。如果只设burst=5,不设nodelay,那么超出的前5个请求会被放入队列延迟处理,用户会感觉变慢。加上nodelay,这5个请求会立刻被处理,但同时也立刻开始计算下一个请求的等待时间,攻击者依然能瞬间发出5个请求。所以,对于明确要防CC的路径,我的建议是:要么burst设很小(比如1-3)并加nodelay,追求绝对频率限制;要么针对某些接口干脆不用nodelay,让恶意请求去排队,拖慢攻击者效率。
第二板斧:黑白名单,把“熟人”和“恶客”分清
限流是“一刀切”,黑白名单就是“精准打击”。尤其在你已经通过日志分析出攻击IP特征的时候,这招特别管用。
黑名单:把捣蛋鬼关外面。
http {
# 可以定义一个黑名单IP列表文件,方便管理
include blacklist.conf;
}
# 在server区块里
server {
location / {
# 如果IP在黑名单,直接返回403
if ($blacklisted_ip) {
return 403;
}
... 其他配置 ...
}
}
blacklist.conf文件里可以这样写(使用geo模块):
geo $blacklisted_ip {
default 0;
# 把要封禁的IP或网段设为1
123.123.123.123 1;
124.124.124.0/24 1; # 封禁整个C段
}
更灵活的方式是结合map模块,动态一点:
map $remote_addr $is_badguy {
default 0;
"~*^(123\.123\.|124\.124\.)" 1; # 用正则匹配IP段
}
server {
if ($is_badguy) {
return 444; # 444是Nginx特有的,直接关闭连接,不返回任何内容,更省资源
}
}
白名单:给自己人开绿灯。这个在维护后台、API接口时特别有用。
geo $is_trusted {
default 0;
192.168.1.0/24 1; # 公司内网
你的家庭公网IP 1; # 在家办公
}
server {
location /admin/ {
if ($is_trusted != 1) {
# 非白名单IP访问管理后台,直接拒绝
return 403;
# 或者更狠一点,重定向到一个迷惑性的错误页
# rewrite ^ /404.html;
}
... 代理到后端管理程序 ...
}
}
白名单的优先级一定要高于限流规则!不然你自己可能都被自己的限流给挡在外面,那可就闹笑话了。通常顺序是:先判断黑白名单,再执行限流。
实战组合拳:一个真实场景的配置思路
假设你有一个博客,评论系统老是被人用脚本刷垃圾评论。你怎么弄?
- 定位攻击点:看日志,发现是
/wp-comments-post.php这个接口被高频POST请求攻击。 - 施加精准限流:单独给这个location加上严格的限流。
location ~ /wp-comments-post\.php$ { # 评论提交,每个IP每秒只能请求1次,不允许突发(burst=0) limit_req zone=perip burst=0 nodelay; # 同时,可以针对这个URI设置一个更小的全局并发限制 limit_conn perip_comment 3; # 每个IP同时最多3个连接 ... fastcgi配置 ... } - 识别并封禁:分析一段时间日志,把那些频繁返回403、404的IP(可能是扫描器)或者评论内容有明显规律的IP段,加入黑名单。
- 保护正常用户:确保静态资源(主题图片、CSS)的location限流宽松,或者不做限流。
几个“踩过坑”才懂的提醒
- 别光盯着“平均速率”(rate):CC攻击往往是脉冲式的。
burst参数就是用来应对这种突发的。设得太死板,一个正常用户多刷新几下页面可能就被拒了。 - 日志是你的眼睛:一定要把被限流拒绝的请求记录下来。在
limit_req后面加上limit_req_status 503;(默认就是503),然后在access_log里就能看到哪些IP老返回503。这是你完善黑白名单最重要的依据。 - 动态IP和代理池是难题:现在的攻击很多用代理IP或者云主机,IP海量变化。纯IP限流和黑名单效果会打折扣。这时候,
limit_req_zone的键值可以换,比如用$http_x_forwarded_for(如果前面有代理)的最后一段,或者结合$http_user_agent(虽然UA可伪造)来综合判断。更高级的可以上limit_req zone=perip rate=5r/s key=${binary_remote_addr}${http_user_agent}这种组合键(需要OpenResty或Lua模块支持)。 - “一刀切”可能误伤:公共WiFi(比如机场、咖啡馆)、学校/企业出口NAT,可能成千上万人用一个出口IP。你的限流如果太狠,会把所有正常用户都挡住。对于这种,白名单机制或者针对特定URL放宽限制就很重要。
- 测试!测试!测试!:配置改完,千万别直接在生产环境重启Nginx。先用
nginx -t测试语法。有条件的话,在测试环境用ab、wrk或者jmeter模拟一下并发请求,看看效果和日志是否符合预期。
说到底,Nginx的防CC配置就像给你的网站大门加装了一套“智能门禁+流量阀门”。它不贵,也不花哨,但足够有效。在考虑那些昂贵的高防方案之前,先把自家门口的这块阵地经营好,往往能解决大部分头疼的问题。
安全这事儿,从来就没有一劳永逸。攻击手段在变,你的策略也得跟着调整。多看看日志,多分析分析异常流量,这套配置用熟了,你对自己网站的抗压能力心里自然就有底了。
行了,配置大概就这些,细节还得你自己根据业务慢慢调。有什么坑或者更好的招,咱们评论区接着聊。

