## 问题场景
某天早上起来,发现网站打不开了。SSH 上去一看,CPU 100%,Nginx 日志每秒几百条请求。原来是被某个爬虫或者恶意扫描盯上了。
Nginx 自带了强大的限流模块,配置好之后可以有效防止这种情况。
## 限制请求速率
在 http 块中定义限流区域:
```nginx
http {
# 定义一个限流区域:名为 mylimit,基于客户端 IP,速率 10r/s
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
}
```
应用到具体 location:
```nginx
location / {
limit_req zone=mylimit burst=20 nodelay;
}
```
参数解释:
- `zone=mylimit`:使用上面定义的限流区域
- `burst=20`:允许 20 个突发请求排队等待
- `nodelay`:超出 rate 的请求立即返回 503,不排队
## 限制并发连接数
```nginx
http {
limit_conn_zone $binary_remote_addr zone=connlimit:10m;
}
server {
location /download/ {
limit_conn connlimit 5; # 每个 IP 最多 5 个并发连接
limit_rate 500k; # 每个连接限速 500KB/s
}
}
```
## 实战配置:给博客的完整防护
```nginx
http {
# 请求频率限制
limit_req_zone $binary_remote_addr zone=global:10m rate=30r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=3r/m;
# 连接数限制
limit_conn_zone $binary_remote_addr zone=perip:10m;
server {
# 全局限制
limit_req zone=global burst=50 nodelay;
limit_conn perip 20;
# 登录接口严格限制
location /admin/ {
limit_req zone=login burst=2 nodelay;
}
# 静态资源不做限流
location ~* \.(jpg|png|css|js|woff2)$ {
limit_req off;
limit_conn off;
}
}
}
```
## 白名单:放过搜索引擎
```nginx
# 定义白名单用的 geo 映射
geo $limit_whitelist {
default 0;
127.0.0.1 1;
# 你的 IP
1.2.3.4 1;
}
# 白名单内的 IP 不做限制
map $limit_whitelist $limit_key {
0 $binary_remote_addr;
1 "";
}
limit_req_zone $limit_key zone=mylimit:10m rate=10r/s;
```
## 被限流时的用户体验
被限流后 Nginx 默认返回 503。可以自定义错误页面:
```nginx
error_page 503 /503.html;
location = /503.html {
internal;
}
```
限流不是目的,保护服务稳定才是。建议先用宽松的阈值,观察日志后再逐步收紧。