⭐ 推荐:社区规则条款 V1.0

Nginx limit_req 防爬虫参数怎么调才不误封?

周末
周末 萌新站长
发布于 2026-10-20 05:40 ·7 浏览 ·0 回复
内容摘要

调 Nginx limit_req 防爬虫应按真实客户端 IP 限流,按页面请求量估算 rate,用 burst 与 delay 吸收并发,并为搜索引擎和监控留空 key 白名单;页面建议 5r/s、burst=20、delay=10,登录

调 limit_req 防爬虫不误封的核心就一句话:**先把限流 key 换成真实客户端 IP,再按「页面请求量」而不是「单请求」估算 rate,然后用 burst + delay 吸收页面并发,最后给搜索引擎和自家监控留空 key 白名单**。只照抄 rate=1r/s burst=5 这种配置,正常用户刷两下页面就会被 503。

limit_req 的三个参数分别管什么?

结论:rate 管平均速率,burst 管瞬时并发,nodelay / delay 管突发请求是立刻处理还是排队。

配置由 limit_req_zone 和 limit_req 两部分组成:

limit_req_zone $binary_remote_addr zone=perip:10m rate=5r/s;

server {
    location / {
        limit_req zone=perip burst=20 delay=10;
        limit_req_status 429;
    }
}
  • rate 支持 r/s 和 r/m 两种单位,内部按毫秒粒度计算,rate=5r/s 约等于每 200ms 放行一个请求。
  • burst 是允许超出的请求数,超出 burst 的请求直接按 limit_req_status 返回(默认 503)。
  • zone 大小决定能记多少 IP:1MB 约存 16000 个状态,10MB 约 16 万。用 $binary_remote_addr(4 字节)而不是 $remote_addr(字符串),内存占用差好几倍。
  • 结论:limit_req_status 429 比默认 503 更好用,能和后端真实故障区分开,也符合 HTTP 语义。

rate 和 burst 怎么定才不误封正常用户?

结论:限流是对「请求」计数,不是对「页面」计数,一个首屏常带 20~60 个请求,所以 rate 必须按请求量算,不能按访客数算。

按这个起点调,再根据日志微调:

场景rateburst说明
HTML 页面5r/s20,delay=10首屏资源多的站点可放宽到 10r/s
登录/注册/搜索接口1r/s5,不加 nodelay让请求排队变慢,而不是直接拒绝
静态资源50r/s100,nodelay优先交给 CDN,源站不限流
开放 API10r/s20,nodelay按业务 SLA 定

估算方法:用 access log 统计单个 IP 在 10 秒窗口内的峰值请求数,取 P99 再留 2 倍余量作为 burst。例如 P99 是 8 个请求 /10 秒,burst 至少给 16。

nodelay 和 delay 怎么选?

结论:nodelay 防误封效果最好,但会给爬虫「无限速」的错觉;delay=N(Nginx 1.15.7 起支持)是折中方案,前 N 个突发请求立即处理,超出的按 rate 排队。

  • 纯 nodelay:burst 内请求全部秒回,用户无感,但爬虫只要控制并发就能一直薅。
  • 纯 delay(不写 nodelay):所有 burst 请求都被平滑,页面会明显变慢,容易误伤。
  • burst=20 delay=10:前 10 个突发立即处理,第 11~20 个排队,第 21 个起返回 429。对人友好,对爬虫有成本。

怎么放行搜索引擎爬虫和自家监控?

结论:用 map 构造空 key,空 key 的请求不会被限流计数——这是 nginx 官方支持的免限流写法。

map $http_user_agent $limit_key {
    default          $binary_remote_addr;
    ""               "";
    ~*(Googlebot|Bingbot|Baiduspider|YisouSpider) "";
    ~*(UptimeRobot|Zabbix)                        "";
}

limit_req_zone $limit_key zone=perip:10m rate=5r/s;

注意两点:UA 可以伪造,重要站点应对 Googlebot/Baiduspider 做反向 DNS 校验(nslookup 查 IP 反解域名是否在 googlebot.com、baidu.com 域下);IP 白名单用 geo $whitelist { default 0; 10.0.0.0/8 1; } 配合同样方式给空 key。

最经典的误封原因是什么?

结论:站点在 CDN 或 SLB 后面却没还原真实 IP,所有用户共用一个回源 IP 作为限流 key,几百人同时访问就被限死。

修法是配 real_ip 模块,并确保 set_real_ip_from 只信任你自己的代理段:

set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

real_ip 生效后 $binary_remote_addr 才是用户真实 IP。除此之外,另外三个高频误封原因是:location 里写了 limit_req 就不会继承上层的(不同 location 记得各自配)、zone 太小导致状态被 LRU 淘汰后重新计数、以及 SPA 单页应用切换路由时一次性打出几十个 XHR 请求。

怎么先观察不拦截地调参?

结论:开源版没有 limit_req_dry_run(该参数是 NGINX Plus 专有),用「高 burst + 日志统计」代替灰度。

做法是先把 limit_req_log_level notice 打开,把 burst 调到目标值的 5 倍跑 24 小时,然后统计 error log 中 limiting requests, excess: X by zone "perip" 的条数和对应 IP,按 P99 反推真实需要的 rate/burst,再收敛参数。压测可以用 wrk -t4 -c50 -d30s 模拟正常用户并发,验证首屏资源加载不被 429。

收尾一句:先修 real_ip、再按请求量定 rate、用 burst+delay 吸收首屏并发、给爬虫和监控留空 key,四件事做完,limit_req 才既拦得住爬虫又封不到人。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2308.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 0

还没有回复,来抢沙发~