直接用limit_req易被绕过,因仅按IP限流难防代理、CDN及IP轮换攻击;需组合IP、URI、User-Agent等多维度限流,并避免burst+nodelay透传洪峰至后端。

为什么直接用 limit_req 很容易被绕过
很多人配完 limit_req zone=ddos burst=10 nodelay 就以为万事大吉,结果发现恶意请求还是打满后端。根本原因在于:默认只按 $binary_remote_addr 限流,而真实攻击常走代理、CDN 或大量 IP 轮换。爬虫更会伪造 User-Agent、随机 X-Forwarded-For,甚至复用合法用户 Cookie 绕过 IP 限制。
真正有效的策略必须组合多个维度识别异常行为:
- 对未登录用户或无有效 Cookie 的请求,额外基于
Host+URI+User-Agent做二级限流 - 对已带认证头(如
Authorization)或有效 session cookie 的请求,放宽限制但监控其请求路径分布 - 拒绝明显异常的请求头组合,比如
User-Agent: python-requests/2.28却携带浏览器常用Accept-Encoding: gzip, deflate
如何配置多粒度 limit_req_zone 并正确引用
单个 zone 不够用,必须定义至少两个 zone:一个保底 IP 级,一个业务级(如按接口路径聚类)。注意 key 表达式里不能直接用正则,但可以用 map 预处理。
示例配置片段:
map $request_uri $req_key {
~^/api/v1/orders/ "api_orders";
~^/api/v1/products/ "api_products";
default $binary_remote_addr;
}
<p>limit_req_zone $binary_remote_addr zone=ip_limit:10m rate=5r/s;
limit_req_zone $req_key zone=api_limit:10m rate=30r/m;</p><p>server {
location /api/ {
limit_req zone=ip_limit burst=10 nodelay;
limit_req zone=api_limit burst=50 nodelay;
proxy_pass <a href="https://www.php.cn/link/65b5b8d1f89bf53a5713bc3afdd83e9e">https://www.php.cn/link/65b5b8d1f89bf53a5713bc3afdd83e9e</a>;
}
}</p>关键点:
-
map必须在http块顶层定义,不能放在server内 - 两个
limit_req指令会叠加生效,任一触发即返回503 Service Temporarily Unavailable -
burst值不是越大越好——过大的 burst 会让突发流量穿透到后端,建议按后端单实例 QPS * 1.5 设定
怎么让爬虫和自动化工具“主动暴露自己”
与其被动拦截,不如设计几个轻量陷阱让脚本自曝身份。Nginx 本身不解析 JS/Cookie,但能靠响应头 + 请求特征做初步筛选。
常用手段:
- 对无
Accept头或Accept: */*的请求,加严限流(真实浏览器必带具体Accept) - 对
User-Agent包含python-requests、curl、httpclient等关键词的,直接跳转到limit_req zone=bot_trap(rate=1r/m) - 对连续 3 次请求
/robots.txt或/favicon.ico后立刻请求深度 API 的客户端,用stickycookie 标记并降权
注意:limit_req 不支持条件触发,所以要用 if + error_page 绕行:
if ($http_user_agent ~* "(python-requests|curl|httpclient)") {
set $limit_key "bot_$binary_remote_addr";
}
limit_req zone=bot_trap key=$limit_key;
为什么 burst 和 nodelay 组合可能压垮后端
看起来 burst=100 nodelay 能扛住突发,但实际是把瞬时洪峰原样转发给上游——Nginx 只是不排队,不代表不透传。后端若没做好熔断,一次 burst 就可能触发雪崩。
更稳妥的做法:
- 去掉
nodelay,让 Nginx 缓冲并匀速放行(配合burst+delay参数) - 对写操作接口(如
POST /api/v1/submit),强制使用limit_req zone=write_limit burst=5,且禁止nodelay - 用
limit_req_status 429替代默认 503,让前端可区分“忙”和“错”,避免重试放大压力
真正难的是动态调整阈值——IP 黑名单、UA 指纹、请求熵值这些,Nginx 做不了,得靠 WAF 或前置 Lua 脚本。别指望单靠 limit_req 拦住所有应用层攻击。


















