跳到主要内容

限流-防盗链-访问控制

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=5burst 中前 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(直接地址栏访问)
blockedReferer 被代理去掉了
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)被连坐

14. 延伸阅读