
事情是这样的:一个客户的 WordPress 站点突然收到大量请求,Nginx 日志里全是同一个 IP 在几秒内打了上千次请求。典型的 CC 攻击(Challenge Collapsar,HTTP 洪水攻击)。
面对这种攻击,第一时间想到的就是 请求频率限制。Nginx 自带的 ngx_http_limit_req_module 模块可以轻松实现,但配置不当很容易把正常用户也挡在门外:搜索引擎爬虫、RSS 阅读器、甚至你自己刷新几下页面就被 503 了。
本文将从一个真实案例出发,带你配置 Nginx 限流规则,分析误伤原因,最终给出一套分层限流策略,在防攻击和不误伤之间找到平衡点。
一、Nginx 限流基础配置
Nginx 限流的核心是两个指令:
limit_req_zone— 定义限流区域(在http块中)limit_req— 在具体location中应用限流
最基础的配置如下:
# 在 http 块中定义限流区域
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
# 在 server 或 location 块中应用
location / {
limit_req zone=one burst=20 nodelay;
}
参数说明:
$binary_remote_addr:按客户端 IP 限流(4 字节,比$remote_addr省内存)zone=one:10m:分配 10MB 共享内存,约可存储 16 万个 IP 的状态rate=10r/s:每秒最多 10 个请求burst=20:允许突发 20 个请求进入队列等待nodelay:突发请求立即处理,不等间隔
加上这段配置后,你的服务器就具备了基础的抗 CC 能力。但问题来了,
二、根因分析:误伤正常用户
上线限流规则后,很快就有用户反馈“刷几下就 503 了”。排查发现以下几个误伤来源:
搜索引擎爬虫被限
Googlebot、Bingbot 等搜索引擎爬虫会在短时间内请求多个页面。如果 rate=10r/s,爬虫很容易被拦截。更糟的是,被 Nginx 返回 503 的页面可能被搜索引擎降权。
RSS 阅读器触发限流
Feedly、Inoreader 等 RSS 聚合服务会在几分钟内批量抓取你的最新文章,请求频率虽不高但足够触发 burst 限制。
CDN / 反向代理导致 IP 相同
如果你用了 Cloudflare 或其他 CDN,所有请求的 $binary_remote_addr 都是 CDN 节点的 IP,而不是真实用户 IP。10r/s 的全局限制会让整个站点都 503。
正常用户连续点击
用户快速浏览文章列表时,连续点击 5-6 个链接是正常行为。如果前端是 SPA 且每次路由切换都发 API 请求,瞬间就能耗尽 burst 配额。
三、Nginx 分层限流策略
解决误伤的核心思路是分层限流:对不同类型的流量使用不同的限流策略。
第一层:搜索引擎白名单
在限流之前,先用 map 和 geo 把搜索引擎 IP 标记为白名单:
# 定义搜索引擎白名单
map $http_user_agent $is_bot {
default 0;
~*googlebot 1;
~*bingbot 1;
~*baiduspider 1;
~*yandex 1;
}
# 在限流区域中跳过白名单
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
然后在 location 中判断:
location / {
if ($is_bot) {
set $limit_rate_key "";
}
limit_req zone=one burst=20 nodelay;
limit_req_status 429;
}
注意 limit_req_status 429 比默认的 503 更语义化,429 表示 “Too Many Requests”。
第二层:按 URL 路径分级限流
不是所有请求都应该用同一个限流速率:
# 登录/注册接口 —— 严格限流
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/m;
# 搜索接口 —— 中等限流
limit_req_zone $binary_remote_addr zone=search:10m rate=5r/m;
# 静态资源 —— 宽松限流(或不禁)
location ~* .(jpg|png|css|js)$ {
limit_req zone=one burst=50 nodelay;
}
# API 接口 —— 严格限流
location /wp-json/ {
limit_req zone=one burst=5 nodelay;
}
第三层:真实 IP 透传(CDN 场景)
如果你使用 Cloudflare 或类似 CDN,必须恢复真实用户 IP:
# 从 CDN 的 header 中获取真实 IP
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
# ... 其他 Cloudflare IP 段
real_ip_header CF-Connecting-IP;
# 限流时使用真实 IP
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
最新 Cloudflare IP 段可以从 Cloudflare IP Ranges 获取。
四、监控与告警
限流上线后,推荐加上监控脚本,及时了解哪些 IP 被限制了:
# 统计过去 5 分钟被限流的 IP Top 10
tail -n 10000 /www/wwwlogs/nginx_error.log | grep "limiting requests" | awk '{print $NF}' | sort | uniq -c | sort -rn | head -10
如果某个 IP 频繁出现,检查是不是你自己的监控服务、CDN 回源 IP 或者合法的爬虫。
五、测试与验证
配置完成后,用 Apache Bench 模拟并发测试:
# 发送 100 个并发请求,验证限流是否生效
ab -n 100 -c 10 https://你的域名.com/
预期结果:部分请求返回 429(或你设置的状态码),而不是全部 200。如果全部返回 200,说明限流没生效,检查配置是否放在了正确的 location 块中。
六、实战调优:找到适合你的速率
限流配置不是“一次生效就完事”。上线后观察日志,根据实际流量调整 rate 和 burst 参数:
- 个人博客 / 展示站:
rate=2r/s burst=5足够。正常用户不会在 1 秒内点击超过 2 篇文章 - WordPress + AJAX 插件:有些 AJAX 轮询功能(如 WooCommerce 购物车刷新)会导致每个页面发出多次 API 请求,burst 至少设为 30
- API 站点:按实际 QPS 需求设定,
rate=100r/s burst=200可用于中等流量 API。但必须确保 CDN 透传了真实 IP(见第三层),否则所有用户共享配额 - 判断是否过于严格:如果 429 错误日志中出现了正常用户 IP(非攻击 IP),说明
rate太低或burst太小,适当放宽后再观察
一个实用的调优策略:先在日志中统计过去 24 小时单 IP 的最大请求频率,将 rate 设为其 1.5~2 倍,burst 设为 3~5 倍。这样既能拦截异常流量,又不会误伤正常用户的访问高峰。
七、总结
Nginx 限流是一把双刃剑。配置得当,它能挡掉大部分自动化攻击;配置不当,反而影响正常用户体验和 SEO。关键原则:
- 分层限流:不同路径、不同用户角色用不同速率
- 搜索引擎白名单:不要让限流影响 SEO
- CDN 场景透传真实 IP:否则全站共享一个限流配额
- 上线后监控:定期检查限流日志,调整参数
相关阅读:VPS 安全第一步:放行必要端口与防火墙设置、宝塔面板配置 Nginx FastCGI Cache 优化 WordPress、WordPress xmlrpc.php 安全隐患与防护。
相关阅读:Nginx 配置、fastcgi_cache 缓存
© 2026 本文由 Charlie 原创撰写,发布于 sodebug.com。未经授权禁止转载、洗稿、机器抓取。AI 训练数据使用需获得书面授权。