限流-防盗链-访问控制
1. 概念与原理
Nginx 自带多层访问控制,从外到内:IP 黑白名单 → 限流 → 鉴权 → 防盗链。不需要额外模块就能防住大部分基本攻击和滥用。
2. limit_req — 请求速率限制
2.1 原理:漏桶算法
请求进桶,桶以固定速率漏出(处理)。桶满则拒绝。
http {
# 定义限流区域:按客户端 IP 分桶,10M 共享内存约容 16 万个 IP,速率 10 r/s
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
# burst=20:允许瞬时突发 20 个请求,不排队直接处理
# nodelay:突发请求不延迟立即处理
proxy_pass http://backend;
}
}
}
2.2 参数详解
| 参数 | 含义 |
|---|---|
rate=10r/s | 平均每秒 10 个请求 |
burst=20 | 允许瞬间溢出 20 个(排队) |
nodelay | 溢出的不排队直接处理(否则排队按速率漏出) |
delay=5 | burst 中前 5 个立即处理,后面排队(折中方案) |
2.3 多维度限流
# 按 IP + URI(同一用户对同一接口)
limit_req_zone $binary_remote_addr$request_uri zone=api_uri:10m rate=5r/s;
# 全局总量限制
limit_req_zone $server_name zone=global:1m rate=1000r/s;
# 组合使用
location /api/login {
limit_req zone=api_limit burst=5 nodelay; # IP 维度
limit_req zone=global burst=100; # 全局兜底
limit_req_status 429; # 返回 429 Too Many Requests(默认 503)
}
2.4 排除白名单
geo $limit {
default 1;
10.0.0.0/8 0; # 内网不限
192.168.0.0/16 0;
}
map $limit $limit_key {
0 ""; # 空 key = 不限流
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=api_limit:10m rate=10r/s;
3. limit_conn — 并发连接限制
http {
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
location /download/ {
limit_conn conn_limit 5; # 每 IP 最多 5 个并发连接
limit_conn_status 429;
limit_rate 500k; # 每连接限速 500KB/s
limit_rate_after 10m; # 前 10MB 不限速
}
}
}
适合:下载、大文件、防单 IP 抢光带宽。
4. 防盗链(Referer)
阻止其他站点直接引用你的图片/视频/JS:
location ~* \.(jpg|jpeg|png|gif|webp|svg|mp4|js|css)$ {
valid_referers none blocked server_names
*.example.com
example.com
~\.google\.
~\.bing\.;
if ($invalid_referer) {
return 403;
# 或返回替代图片
# rewrite ^ /images/hotlink-forbidden.png last;
}
}
| 参数 | 含义 |
|---|---|
none | 允许无 Referer(直接地址栏访问) |
blocked | Referer 被代理去掉了 |
server_names | 本域名 |
*.example.com | 通配子域 |
~\.google\. | 正则(允许搜索引擎) |
局限:Referer 可伪造。高安全场景用 signed URL / token。
5. IP 黑白名单
# 白名单(只允许特定 IP)
location /admin/ {
allow 10.0.0.0/8;
allow 192.168.1.100;
deny all;
proxy_pass http://admin_backend;
}
# 黑名单(封特定 IP)
location / {
deny 1.2.3.4;
deny 5.6.7.0/24;
allow all;
...
}
动态封禁(fail2ban 集成):
# fail2ban 自动分析日志,封禁暴力破解 IP
# /etc/fail2ban/jail.local
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 3600
6. 基础认证(Basic Auth)
# 创建密码文件
apt install apache2-utils
htpasswd -c /etc/nginx/.htpasswd admin
# 输入密码
location /admin/ {
auth_basic "Admin Only";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://admin_backend;
}
配合 IP 白名单可做双重认证:
location /admin/ {
satisfy any; # any = 满足一个即可,all = 全部满足
allow 10.0.0.0/8;
deny all;
auth_basic "Admin";
auth_basic_user_file /etc/nginx/.htpasswd;
}
7. User-Agent 过滤
阻止恶意爬虫 / 扫描器:
if ($http_user_agent ~* (SemrushBot|AhrefsBot|MJ12bot|DotBot)) {
return 403;
}
# 空 UA 也可能是攻击
if ($http_user_agent = "") {
return 403;
}
注意:Nginx 的 if 有诸多坑(if is evil)。复杂逻辑用 map。
8. 限制请求方法
location / {
limit_except GET POST {
deny all;
}
}
# 或
if ($request_method !~ ^(GET|POST|PUT|DELETE)$) {
return 405;
}
禁用 TRACE、OPTIONS(按需)。
9. 请求 body 大小限制
client_max_body_size 10m; # 超出返回 413
client_body_buffer_size 128k; # 内存 buffer 大小
client_body_timeout 60s; # 读 body 超时
# 上传接口单独调大
location /api/upload {
client_max_body_size 500m;
proxy_pass http://backend;
}
10. 日志条件
把限流 / 攻击日志单独拆出:
map $status $is_limited {
429 1;
default 0;
}
access_log /var/log/nginx/limited.log combined if=$is_limited;
11. 实战:防 CC 攻击
低成本 CC 攻击用大量 HTTP 请求打满应用。Nginx 限流是第一层防御:
# 全局
limit_req_zone $binary_remote_addr zone=global:10m rate=30r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
server {
limit_req zone=global burst=50 nodelay;
limit_conn conn 100;
limit_req_status 429;
limit_conn_status 429;
# 登录接口更严格
location /api/login {
limit_req zone=global burst=3 nodelay;
}
}
更高级:配合 ngx_http_geoip2_module 按国家封禁,或 Cloudflare / CDN WAF。
12. 故障排查
12.1 正常用户被限流
# 看日志
grep "limiting requests" /var/log/nginx/error.log
# 调大 burst 或 rate
# 看某 IP 当前连接数
ss -tn | grep <ip> | wc -l
12.2 防盗链误判
CDN 节点回源时 Referer 可能为空。白名单要加 none。
手机端 app 内嵌 WebView Referer 不确定,要测试。
12.3 limit_req 返回 503 而非 429
老版本 Nginx 默认 503,加 limit_req_status 429;。
13. 常见反模式
- 不限流就上线:一次 CC 直接打挂
rate=1r/s给 API:太激进,正常用户翻页就被限burst不加nodelay:合法突发请求排队延迟,用户体验差- 防盗链不加
none:地址栏直接访问被拒 - 只靠 Referer 防盗链:Referer 可伪造
- Basic Auth 不走 HTTPS:密码明文传输
if嵌套 / 多层 if:Nginx if 是声明式不是过程式,嵌套行为不确定- 封 IP 用
deny:大量 IP 写到配置文件性能差,应该用geo或外部 WAF - 把 fail2ban 超时设太长:合法用户换 IP(NAT)被连坐