一、核心原理
nginx 提供两个核心模块实现限流,各司其职:
| 模块 | 功能 | 算法/机制 | 适用场景 |
|---|---|---|---|
ngx_http_limit_req_module | 限制单个 ip 在单位时间内的请求数量 | 漏桶算法 | 防刷接口、控制 qps |
ngx_http_limit_conn_module | 限制单个 ip 同时建立的连接数量 | 连接计数 | 防 cc 攻击、防连接耗尽 |
漏桶算法:以固定速率处理请求,超出速率的请求排队或被拒绝。burst 定义桶容量,nodelay 决定是否延迟处理。
二、环境准备
系统要求:ubuntu 20.04 / centos 7+,nginx ≥ 1.1.8(当前稳定版均支持)。
# ubuntu sudo apt update && sudo apt install nginx -y # centos sudo yum install epel-release -y && sudo yum install nginx -y
确认模块已编译:
nginx -v 2>&1 | grep -o with-http_limit_req_module nginx -v 2>&1 | grep -o with-http_limit_conn_module
三、基础配置:限制单 ip 请求速率
3.1 定义限流区域(http 块)
http {
# 按客户端ip限流,10mb共享内存(约存16万ip),每秒10个请求
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# 登录接口专用:更严格的限制
limit_req_zone $binary_remote_addr zone=login_limit:5m rate=2r/s;
# 并发连接限制
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
}为什么用
$binary_remote_addr而非$remote_addr? 二进制格式更省内存,推荐生产环境使用。
3.2 应用限流规则(server/location 块)
server {
listen 80;
server_name api.example.com;
location /api/ {
# 应用限流:允许突发20个请求,nodelay立即处理突发,超出部分直接拒绝
limit_req zone=api_limit burst=20 nodelay;
# 自定义限流返回状态码(429比默认503更符合语义)
limit_req_status 429;
proxy_pass http://backend_server;
}
location /api/login {
# 登录接口使用更严格的限流
limit_req zone=login_limit burst=5 nodelay;
limit_req_status 429;
proxy_pass http://auth_service;
}
}参数深度解析:
rate=10r/s:这是长期的平均速率,也可写30r/m(每分钟30次)burst=20:当请求瞬间超过 10r/s 时,多出来的请求会先进入“突发桶”排队。nodelay:加上这个参数,突发桶里的 20 个请求会立即被处理,而不是按每秒 10 个的速度排队。如果不加nodelay,请求会被延迟处理,适合对实时性要求不高的场景。
四、组合防护:速率 + 连接双限制
生产环境推荐同时启用双重保护:
http {
# 速率限制区
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=50r/s;
# 连接数限制区
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
location /api/ {
proxy_pass http://backend_server;
# 速率限制:每秒50个,允许突发100个
limit_req zone=api_limit burst=100 nodelay;
# 连接数限制:单个ip最多同时保持50个连接
limit_conn conn_limit 50;
limit_conn_status 429;
}
}
}五、精细化限流策略
5.1 按接口路径限流(防扫描)
防止扫描器遍历 api,对每个 uri 单独计数:
http {
# $uri 不包含查询参数,如需区分参数用 $request_uri
limit_req_zone $uri zone=per_uri:10m rate=10r/s;
}
server {
location / {
limit_req zone=per_uri burst=20 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
}5.2 动态分级限流(vip用户)
通过 map 实现不同用户等级的差异化限流:
http {
# 从请求头获取用户等级(需后端传递)
map $http_x_user_level $rate_limit {
"vip" "100r/s";
"normal" "10r/s";
default "2r/s";
}
limit_req_zone $binary_remote_addr zone=dynamic:10m rate=$rate_limit;
server {
location /api/ {
limit_req zone=dynamic burst=5 nodelay;
proxy_pass http://backend;
}
}
}注:
rate使用变量需 nginx 1.15.10+。
5.3 下载限速 + 并发限制
location /download/ {
limit_conn conn_limit 2; # 单ip最多2个并发下载
limit_rate 500k; # 单连接限速500kb/s
alias /data/files/;
}5.4针对敏感接口
登录和验证码接口是暴力破解的重灾区,需要设置更严格的规则:
http {
# 专门为登录接口定义一个更严格的限流区:每秒仅允许 2 次请求
limit_req_zone $binary_remote_addr zone=login_limit:5m rate=2r/s;
server {
# 普通接口
location /api/ {
limit_req zone=api_limit burst=50 nodelay;
proxy_pass http://backend_server;
}
# 登录接口(单独配置)
location /api/login {
limit_req zone=login_limit burst=5 nodelay;
limit_req_status 429;
proxy_pass http://backend_server;
}
}
}六、白名单与动态控制
6.1 白名单配置(geo + map 技巧)
nginx 的 limit_req 不能直接通过 if 开关,需用 zone 切换实现"跳过限制":
http {
# 定义白名单
geo $whitelist {
default 0;
203.0.113.0/24 1; # 公司出口ip段
192.168.1.100 1;
}
# 正常限流区
limit_req_zone $binary_remote_addr zone=global:10m rate=20r/s;
# 虚拟高速区(白名单ip使用,实际不限流)
limit_req_zone $binary_remote_addr zone=dummy:1m rate=10000r/s;
# 根据白名单动态选择zone
map $whitelist $limit_zone {
1 "dummy";
0 "global";
}
# 登录专用区
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
map $whitelist $login_zone {
1 "dummy";
0 "login";
}
# 并发限制
limit_conn_zone $binary_remote_addr zone=connip:10m;
# 日志格式增加限流信息
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" limit_req_status=$limit_req_status';
server {
listen 80;
server_name api.example.com;
access_log /var/log/nginx/api_access.log main;
limit_req_status 429;
limit_conn_status 429;
# 全局接口
location / {
limit_req zone=$limit_zone burst=30 nodelay;
limit_conn connip 20;
proxy_pass http://backend;
}
# 登录接口
location /api/login {
limit_req zone=$login_zone burst=3 nodelay;
proxy_pass http://auth;
}
# 下载接口
location /download/ {
limit_conn connip 2;
limit_rate 500k;
alias /data/downloads/;
}
}
}七、cdn / 反向代理场景
如果 nginx 前有 cdn 或 nat,需获取真实客户端 ip:
# 配置真实ip来源 set_real_ip_from 10.0.0.0/8; # cdn节点网段 set_real_ip_from 172.16.0.0/12; real_ip_header x-forwarded-for; # 使用真实ip限流 limit_req_zone $http_x_forwarded_for zone=cdn_limit:10m rate=20r/s;
八、友好的限流提示
自定义 429 错误页面,返回 json:
server {
# 自定义限流错误
error_page 429 = @rate_limited;
location @rate_limited {
default_type application/json;
return 429 '{"code": 429, "msg": "请求过于频繁,请稍后再试"}';
}
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
}九、测试与验证
9.1 apachebench (ab)
# 安装 sudo apt install apache2-utils # ubuntu sudo yum install httpd-tools # centos # 发送100个请求,并发10个 ab -n 100 -c 10 http://api.example.com/api/test
9.2 curl 循环测试
for i in {1..100}; do
http_code=$(curl -s -o /dev/null -w "%{http_code}" http://api.example.com/api/test)
echo "request $i: $http_code"
done9.3 日志排查
# 实时查看限流日志 tail -f /var/log/nginx/error.log | grep "limiting requests" # 统计429状态码数量 grep " 429 " /var/log/nginx/access.log | wc -l # 查看限流状态(若配置了日志格式) tail -f /var/log/nginx/api_access.log
十、进阶优化
10.1 调整日志级别
limit_req_log_level error; # 默认warn,可选info/notice/warn/error limit_conn_log_level error;
10.2 结合 fail2ban 自动封禁
配置 fail2ban 监控 429 日志,超限直接 iptables 封禁:
# /etc/fail2ban/jail.local [nginx-req-limit] enabled = true filter = nginx-req-limit action = iptables-multiport[name=reqlimit, port="http,https"] logpath = /var/log/nginx/api_access.log maxretry = 10 findtime = 60 bantime = 600
# /etc/fail2ban/filter.d/nginx-req-limit.conf [definition] failregex = ^<host> -.* 429 ignoreregex =
10.3 平滑重载配置
sudo nginx -t && sudo systemctl reload nginx
十一、完整生产配置示例
http {
# ========== zone 定义 ==========
limit_req_zone $binary_remote_addr zone=global:20m rate=20r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=dummy:1m rate=10000r/s;
limit_conn_zone $binary_remote_addr zone=connip:10m;
# ========== 白名单 ==========
geo $whitelist {
default 0;
203.0.113.0/24 1;
}
map $whitelist $limit_zone {
1 "dummy";
0 "global";
}
map $whitelist $login_zone {
1 "dummy";
0 "login";
}
# ========== 日志 ==========
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" limit_req_status=$limit_req_status';
server {
listen 80;
server_name api.example.com;
access_log /var/log/nginx/api_access.log main;
limit_req_status 429;
limit_conn_status 429;
# 全局api
location /api/ {
limit_req zone=$limit_zone burst=30 nodelay;
limit_conn connip 20;
proxy_pass http://backend;
}
# 登录(更严格)
location /api/login {
limit_req zone=$login_zone burst=3 nodelay;
proxy_pass http://auth;
}
# 下载
location /download/ {
limit_conn connip 2;
limit_rate 500k;
alias /data/downloads/;
}
# 限流提示
error_page 429 = @rate_limited;
location @rate_limited {
default_type application/json;
return 429 '{"code":429,"msg":"请求过于频繁,请稍后再试"}';
}
}
}十二、关键注意事项
| 要点 | 说明 |
|---|---|
| 共享内存大小 | 10mb 约存16万ip,日活大需调大 |
| burst + nodelay | 不加 nodelay 会延迟排队,加则直接丢弃超限请求 |
| cdn/代理 | 必须配置 real_ip_header 获取真实ip,否则误伤 |
| 阈值调优 | 根据后端实际抗压能力和正常用户习惯动态调整 |
| 限流非目的 | 本质是为后端争取恢复空间,需配合监控和自动封禁 |
通过合理组合 limit_req 和 limit_conn,配合白名单、分级限流、友好提示和 fail2ban 联动,可以为 api 构建一道低成本、高效率的防护墙。
到此这篇关于nginx 限流防刷完全指南从原理到生产实战指南的文章就介绍到这了,更多相关nginx 限流防刷内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论