Nginx如何限制访问速率与并发连接数?

来源:菜鸟站长作者:老毕头衔:草根站长
导读:本期聚焦于老毕创作的《Nginx如何限制访问速率与并发连接数?》,敬请观看详情。接口没有限速保护,一个失控的脚本就可能把上游服务打到不可用。Nginx 内置的限流能力集中体现在两个模块上:ngx_http_limit_req_module 负责按请求速率拦截,ngx_http_limit_conn_module 负责按并发连接数拦截。二者基于共享内存区计数,可以在多个 worker 进程间保持一致。本文会从配置文件结构讲起,说明 limit_req_zone、limit_req、limit_conn_zone、limit_conn 等指令的用法和参数含义,并给出面向登录接口、静态资源、下载服务等场景的完整示例。同时会讨论突发流量如何处理、key 该如何选择、以及配置后如何用压测工具验证效果。读完这篇,你可以直接复制配置到自己的 Nginx 上完成基础的访问频率与并发数量管控。

后端接口突然变慢,日志里显示同一个 IP 在几十秒内发起了上千次请求。如果把这种情况交给应用代码去处理,要么改造量大,要么防护点分散。更合适的做法是在接入层 Nginx 上直接拦掉异常流量。Nginx 自带的两个模块提供了非常实用的限流能力:ngx_http_limit_req_module 控制请求速率,也就是每秒允许通过多少个请求;ngx_http_limit_conn_module 控制并发连接数,也就是同一个客户端同时能建立多少个连接。两者配合使用,可以挡住大部分 CC 攻击、爬虫滥用以及单 IP 暴力破解。

Nginx如何限制访问速率与并发连接数?

从实现原理上看,limit_req 使用令牌桶算法,允许一定程度的突发流量,而不是严格按照固定窗口计数。limit_conn 则更简单直接,每个 key 对应一个连接计数器,连接建立时加一,关闭时减一。两个模块都依赖共享内存区域来保存计数状态,因此多个 worker 进程之间可以共享同一份限流数据。下面会把这两套配置拆开详细说明,并给出可以直接落地的示例。

一、请求速率限制:limit_req_module 配置详解

ngx_http_limit_req_module 的核心思想是根据客户端标识(通常是 IP)维护一个请求频率计数器,超过设定速率就返回错误。配置分为两步:先在 http 层用 limit_req_zone 定义共享内存区和速率,再在 server 或 location 层用 limit_req 启用限制。

limit_req_zone 的语法为 limit_req_zone key zone=名称:大小 rate=速率;。其中 key 一般使用 $binary_remote_addr,它是二进制形式的客户端 IP,比 $remote_addr 字符串更省内存。比如 zone 设置为 10m,大约能存储 16 万个 IP 的状态,对于多数中小站点足够使用。rate 可以写成每秒请求数,如 rate=10r/s,也可以写成每分钟请求数,如 rate=600r/m。需要注意的是,rate 指的是平均速率,不是瞬时峰值。

http {
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

    server {
        listen 80;
        server_name ipipp.com;

        location /api/ {
            limit_req zone=api_limit burst=20 nodelay;
            proxy_pass http://backend_pool;
        }
    }
}

上面的配置表示对每个 IP,访问 /api/ 路径的请求平均速率不能超过每秒 10 个。burst 参数表示允许在短时间内超出平均速率的请求数量,这部分请求会排队等待处理。如果不加 nodelay,排队请求会按照固定间隔依次放行,可能会让部分请求等待较长时间。加上 nodelay 后,排队请求立即被处理,但超出 burst 上限的请求直接返回 503。nodelay 不会改变突发总额,只是让突发请求不再被延迟。需要注意,burst 越大,瞬时能穿透的请求就越多,实际配置时需要结合后端承受能力权衡。

默认的返回状态码是 503,如果希望返回更明确的状态码,可以用 limit_req_status 429; 改成 429 Too Many Requests。此外,可以通过 limit_req_log_level 设置日志级别,默认是 error,建议保留,方便观察哪些请求被限流了。

二、并发连接数限制:limit_conn_module 配置详解

请求速率限制关注的是单位时间内的请求数量,而并发连接限制关注的是同时处于活动状态的连接数。有些场景下,请求速率可能很低,但每个连接持续时间很长,比如文件下载、视频播放。这时候仅靠 rate 限制就不够,因为一个慢速连接也能长时间占用资源。limit_conn 模块可以限制同一个 key 同时保持的最大连接数,超过后新的连接会被拒绝。

配置同样分两步,先在 http 层定义共享内存区和 key,再用 limit_conn 指令启用。与 limit_req_zone 类似,limit_conn_zone $binary_remote_addr zone=conn_zone:10m; 定义一个名为 conn_zone 的共享内存区。然后在 location 中使用 limit_conn conn_zone 5;,表示同一 IP 最多同时建立 5 个连接。如果超过这个数量,Nginx 会返回 503 或你指定的状态码。

http {
    limit_conn_zone $binary_remote_addr zone=download_conn:10m;

    server {
        listen 80;
        server_name ipipp.com;

        location /download/ {
            limit_conn download_conn 5;
            limit_rate 200k;
            alias /data/files/;
        }
    }
}

这个示例还额外配置了 limit_rate 200k;,它限制的是每个连接的传输速度,单位是字节每秒,这里表示每个连接最多 200KB/s。limit_rate 和 limit_conn 不是同一个模块,但经常搭配使用:限制连接数防止连接被占满,限制速率防止单个连接把带宽吃完。需要注意 limit_rate 只对响应体生效,请求体不受控制。

如果希望针对不同路径使用不同的并发限制,可以定义多个 limit_conn_zone,例如登录接口限制 2 个连接,下载接口限制 5 个连接。不过要记住,不同的 zone 是独立计数的,同一个 IP 在不同 zone 里的状态互不影响。

三、真实场景下的组合配置示例

实际生产环境往往不是单一规则就能覆盖所有需求。比如一个网站有普通页面、登录接口、文件下载接口,各自对限流的要求差别很大。普通页面可以放宽速率,登录接口要严防暴力破解,下载接口要控制并发和带宽。下面给出一个相对完整的 Nginx 配置片段,展示如何将这些规则组合起来。

http {
    # 全站基础限流,每个 IP 每秒最多 20 个请求
    limit_req_zone $binary_remote_addr zone=base_limit:10m rate=20r/s;
    # 登录接口专用限流,每个 IP 每秒最多 1 个请求
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;
    # 下载并发限制
    limit_conn_zone $binary_remote_addr zone=download_conn:10m;

    server {
        listen 80;
        server_name ipipp.com;

        # 普通页面限流
        location / {
            limit_req zone=base_limit burst=30 nodelay;
            root /var/www/html;
        }

        # 登录接口限流
        location /login {
            limit_req zone=login_limit burst=3 nodelay;
            limit_req_status 429;
            proxy_pass http://backend_pool;
        }

        # 下载接口并发限制
        location /download/ {
            limit_conn download_conn 5;
            limit_rate 100k;
            alias /data/files/;
        }
    }
}

在这个配置里,普通页面允许每秒 20 个请求,可以突发 30 个,适合应对正常的浏览器并发请求。登录接口被限制到每秒 1 个请求,突发 3 个,能有效阻止密码爆破。下载接口不限制请求速率,但限制同一 IP 最多 5 个连接,并且每个连接限速 100KB/s,避免带宽被少数用户占满。

需要特别提醒,登录接口如果使用了 CDN 或反向代理,客户端 IP 可能变成代理节点的 IP。此时直接用 $binary_remote_addr 会把所有经过同一个代理的用户算成一个 key,导致误伤。这种情况下应该改用 $http_x_forwarded_for 来获取真实 IP,但这个头可以被伪造,且长度较大,需要在限流准确性和内存占用之间做选择。一般建议在 Nginx 前端自行处理真实 IP 识别,然后使用一个内部自定义变量作为限流 key。

四、如何测试与验证限流效果

配置完成后不能凭感觉判断是否生效,需要用工具压测。ApacheBench(ab)是最常见的工具之一,可以通过并发数和总请求数来观察返回码。例如对一个速率限制为 10r/s 的接口,使用 ab -n 1000 -c 50 http://ipipp.com/api/test,结果中非 2xx 的响应比例应该明显上升,同时 Nginx 的 error.log 里会出现类似 limiting requests 的记录。

# 模拟 1000 个请求,50 个并发
ab -n 1000 -c 50 http://ipipp.com/api/test

# 查看限流日志
tail -f /var/log/nginx/error.log | grep limiting

除了 ab,wrk 和 curl 循环也可以用来做简单验证。wrk 更适合长连接下的压力测试,能更真实地反映并发连接限制的效果。例如 wrk -t 4 -c 100 -d 30s http://ipipp.com/download/test.iso,如果 limit_conn 设置为 5,那么同时处于活动状态的连接数不会超过 5,其余连接会收到 503 或被排队。

验证时需要关注几个点:被限流的请求返回的状态码是否符合预期;正常流量是否被误伤;突发的请求在 burst 范围内是否能快速处理;不同 worker 进程下的计数是否一致。如果发现限流没有生效,检查配置是否在正确的层级,以及 zone 名称是否拼写一致。limit_req_zone 和 limit_req 中的 zone 名称必须完全匹配,否则 Nginx 会报错或忽略配置。

五、限流配置的常见误区和优化建议

一个常见的误区是把 rate 设置得过高或者 burst 设置得过大,导致限流形同虚设。rate 是平均速率,burst 是允许的突发量。如果 rate=100r/s 且 burst=200,对于大多数中小站点来说已经很难触发限制。实际配置应该根据后端服务的真实承受能力来设定,而不是拍脑袋。可以先从保守值开始,比如全站 rate=20r/s,登录接口 rate=1r/s,然后根据监控数据逐步调整。

另一个容易忽略的问题是共享内存的容量。limit_req_zone 和 limit_conn_zone 后面的 size 参数决定了能存储多少个 key 的状态。如果内存区满了,Nginx 会返回 503 并记录错误,但这可能不是我们想要的限流行为。通常每个 key 大约占用 64 字节到 100 字节,如果需要记录 10 万个 IP,10m 内存基本够用。但如果网站访问量很大,建议预留 50m 以上,并定期观察共享内存使用情况。

另外,limit_req 和 limit_conn 可以叠加使用,但不要给所有请求都加上非常严格的限制,否则会降低用户体验。更合理的做法是对核心接口单独设置严格限流,对静态资源只做连接数和带宽限制,对普通页面保持较宽松的速率。同时可以为搜索引擎爬虫设置白名单,避免误伤正常抓取。Nginx 本身没有直接的白名单指令,但可以通过 map 或 if 结合控制,不过要注意 if 在 location 中可能导致一些副作用,建议优先使用 geo 模块配合自定义变量。

最后,限流是防御手段之一,不能替代应用层的安全校验和资源优化。如果后端服务本身存在性能瓶颈,限流只能延缓问题爆发,并不能根治。真正健壮的系统应该在上游做好扩容、缓存和熔断,在接入层用 Nginx 限流来兜底。这样即使遇到突发流量,也能保证大部分用户正常使用。

Nginx限流并发连接限制请求速率限制修改时间:2026-09-29 20:05:55

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0929/63532.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。