Nginx 限流配置教程:limit_req 与 limit_conn 防 CC 攻击的实操步骤与常见问题

适用场景与准备工作

网站遭遇 CC 攻击或者被恶意爬虫高频抓取时,最直接的表现就是服务器 CPU 飙高、带宽被打满,正常用户访问变慢甚至打不开。Nginx 自带的 ngx_http_limit_req_module 和 ngx_http_limit_conn_module 两个模块,可以在应用层对请求频率和并发连接做限制,把异常流量挡在业务之外,是站长和运维最常用的基础防护手段之一。

开始操作前有三点准备:第一,限流只能缓解攻击、不能根除 DDoS,大规模攻击仍建议配合云厂商的高防 IP 或 WAF;第二,修改任何 Nginx 配置前先备份原配置文件,一条 cp 命令即可,出问题能立刻回滚;第三,本文以 Ubuntu/CentOS 上标准方式安装的 Nginx 为例,宝塔面板用户在「软件商店 → Nginx → 配置修改」中编辑的也是同一份 nginx.conf,配置原则完全一致。

顺便区分两个概念:CC 攻击发生在应用层,攻击者伪装成正常用户反复请求动态页面,流量不一定大,却能拖垮数据库和 PHP 进程;而大流量 DDoS 主要堵带宽,靠应用层配置挡不住。Nginx 限流针对的正是前者,这也是它在站长圈子里常被当作第一道防线的原因。

第一步:认识两个核心模块

limit_req_zone 用来定义一块共享内存区域和限制规则,它只能放在 http 块中,不能写进 server 或 location,否则 Nginx 会提示 "limit_req_zone directive is not allowed here"。其中 key 一般使用 $binary_remote_addr(客户端 IP),rate 是允许的请求速率,单位支持 r/s(每秒请求数)和 r/m(每分钟请求数)。

limit_req 则是在 server 或 location 中引用上面定义的 zone,是真正生效的指令。与它对应,limit_conn_zone 定义并发连接的内存区域,limit_conn 引用并限制单个 key 同时建立的连接数,常用于限制单个 IP 的下载并发,防止有人用下载工具把带宽占满。两者一个管频率、一个管并发,可以叠加使用。另外,zone=req_one:10m 里的 10m 是共享内存大小,大约可以记录十几万个客户端 IP 的状态,普通站点完全够用,流量特别大的站点可以适当调大。

第二步:配置请求频率限制

打开 Nginx 主配置文件(一般在 /etc/nginx/nginx.conf),在 http 块内加入 zone 定义:

limit_req_zone $binary_remote_addr zone=req_one:10m rate=10r/s;

然后在需要保护的 server 或 location 中引用它:

limit_req zone=req_one burst=20 nodelay;
limit_req_status 503;

参数含义:rate=10r/s 表示平均每秒允许同一 IP 发起 10 个请求,burst=20 表示允许 20 个请求的突发排队,nodelay 表示排队中的请求立即处理而不是逐个延迟放行。对普通企业站点,rate 设在 5r/s 到 20r/s 之间、burst 留 10 到 30 比较稳妥,设置过低会把正常用户一起挡在门外。改完后先执行 nginx -t 校验语法,确认无误再执行 systemctl reload nginx 平滑重载,不要直接 restart,避免中断现有连接。

规则上线后如何确认生效?可以在服务器上对自己的站点做一次温和测试,例如连续请求首页并查看返回码:

for i in {1..50}; do curl -s -o /dev/null -w "%{http_code}\n" https://你的域名/; done

如果返回码开始出现 503,同时 error.log 中出现 "limiting requests, excess: ..." 记录,说明限流已经按预期工作。测试只打自己的网站、频率不要过高,确认后即可停止。

数据中心服务器与日志监控
Nginx 限流是网站防 CC 攻击的基础层,配合日志监控可持续观察拦截效果

第三步:限制并发连接数

频率限制之外,再限制单个 IP 的并发连接数,对下载站、图片站、资源站尤其有效。同样在 http 块中定义内存区域:

limit_conn_zone $binary_remote_addr zone=conn_one:10m;

在 server 或 location 中引用:

limit_conn conn_one 10;
limit_conn_status 429;

这行表示同一个 IP 同时最多保持 10 个连接,超出部分直接返回 429。这里要特别注意:浏览器打开一个网页往往会同时发起多个请求(HTML、CSS、JS、图片一起加载),所以 limit_conn 的数值不宜低于 10,否则用户会频繁遇到页面打不开、图片加载失败。建议先用 20 左右观察,再根据日志逐步收紧。如果网站提供文件下载,可以只在下载目录的 location 上启用 limit_conn,不影响正常页面浏览。

第四步:白名单与精细化策略

一刀切限流容易误伤,更稳妥的做法是只对敏感位置生效。例如对 WordPress 后台登录页单独限流:

location /wp-login.php {
    limit_req zone=req_one burst=5 nodelay;
}

对于自己的办公 IP、监控探针或合作伙伴,可以用 geo 模块加白名单,让特定 IP 不受限制:

geo $limit_ip {
    default 1;
    123.123.123.123 0;
}
map $limit_ip $limit_key {
    1 $binary_remote_addr;
    0 "";
}

然后把 limit_req_zone 的 key 改成 $limit_key,白名单 IP 的 key 为空字符串,自然不会命中任何限制。另外,如果站点前面套了 CDN 或负载均衡,一定要先用 set_real_ip_from 和 real_ip_header 把真实客户端 IP 取回来,否则限制到的是 CDN 节点 IP,会把同一节点下的所有真实用户一起误伤。每次改完配置,养成三步习惯:nginx -t 校验、systemctl reload nginx 重载、tail -f /var/log/nginx/error.log 观察是否出现 "limiting requests, excess: ..." 日志,确认规则按预期触发。

常见问题

问题一:开启限流后正常用户访问出现 503 怎么办?大概率是 rate 或 burst 设置过低。先把两个参数放宽一倍,观察一天业务日志,确认没有误伤后再逐步收紧;也可以只对 wp-login.php、xmlrpc.php、搜索接口这类敏感路径限流,不动静态资源。

问题二:提示 "limit_req_zone directive is not allowed here" 是什么原因?说明把 zone 定义写进了 server 或 location 块。zone 定义只能放在 http 块,引用指令 limit_req 才能写在 server 或 location 中,两者位置不能颠倒。

问题三:限制不生效,攻击流量照进怎么办?先检查网站前面是否有 CDN 或负载均衡导致取到的 IP 不对,再检查配置是否被其他 server 块匹配。可以用 curl -I 连续请求自己的页面做测试,看是否返回 503、error.log 是否记录 limiting requests。

问题四:限流能彻底防住 CC 攻击吗?不能。limit_req 只能减缓攻击对源站的压力,面对分布式大流量攻击,仍需要高防 IP、WAF 或云防护产品配合。限流的定位是纵深防护的第一层,先把脚本小人和低速 CC 挡在门外,为排查和升级防护争取时间。

总结

Nginx 限流的核心就两条指令:limit_req 控制请求频率,limit_conn 控制并发连接,先备份、再 nginx -t、后 reload 的顺序不能乱。合理的限流策略几乎零成本,却能挡掉大部分恶意爬虫和低速 CC,是每台对外提供服务的服务器都值得配置的基础防护。

最后提醒:限流参数没有放之四海皆准的值,一定要结合自己业务的真实访问曲线调整,改完之后持续观察 error.log 和业务监控,在防护效果与用户体验之间找到平衡点,才能真正做到既安全又不误伤。

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容