未来星球 未来星球
  • 首页
  • 新闻资讯
  • 技术教程
  • 软件工具
  • 知识百科
  • 生活实用
  • 个人随笔
  • 生命外星人
首页 › 技术教程 › 用nginx 1.24挡住刷接口的爬虫:3段配置,和4个必然踩到的坑
  • 0
  • 0

用nginx 1.24挡住刷接口的爬虫:3段配置,和4个必然踩到的坑

admin
8 小时前
4 views
用nginx 1.24挡住刷接口的爬虫:3段配置,和4个必然踩到的坑-未来星球

一个接口被脚本刷的时候,最先出现的不是 502,是响应时间从 80 毫秒变成 4 秒,然后所有人一起骂服务器慢。限流要解决的不是「挡住攻击」,而是「在被打的时候,正常用户还能拿到一句回话」。

nginx 自带两个模块能做完这件事的大部分:limit_req 管速率,limit_conn 管并发。它们不需要装第三方模块,也不需要 Lua。难点从来不在写配置,在于搞清「一个请求」在你这条链路上到底算谁的。

下面这套配置在 Ubuntu 24.04 + nginx 1.24.0(官方 apt 源)上跑通,反向代理一个 FastAPI 服务,实测能在 30 条/秒的脚本压力下,把正常访问的响应时间压在 200 毫秒以内。

1. 先分清三件事,别混着配

限速、限并发、限带宽是三个正交的东西,配错一个,另外两个就白配了。

指令 管什么 典型场景 超限表现
limit_req 单位时间的请求数 登录、搜索、下单接口 429 或排队等待
limit_conn 同时打开的连接数 大文件下载、SSE 长连接 503
limit_rate 单连接的字节速率 视频、附件下载 变慢,不报错

很多人只用 limit_req 去治下载站被拖死的问题,配完发现没用——因为慢速下载的请求数很低,一条连接能耗半小时。速率低不等于压力小,这是限流配置最常见的误判。

2. 第一段:定义共享内存区

限流状态存在共享内存里,所以第一件事是给这块内存起名、定大小、定速率。limit_req_zone 只能写在 http 块里,写在 server 里会直接报错。

http {
    # 每个 IP 每秒 5 个请求;10MB 共享内存约可容纳 8 万个 IPv4 状态
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/s;
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;

    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

    server {
        listen 80;
        server_name example.com;
        ...
    }
}

两个细节决定这块内存够不够用。第一,官方文档给的估算是 1MB 约 16000 个状态,64 位系统上实际按一半算,也就是 8000 左右;10MB 对应 8 万个 IP。按「日活 IP 数 × 2」估容量,比按请求数估靠谱得多。

第二,用 $binary_remote_addr 而不是 $remote_addr。前者是 4 字节的二进制,后者是字符串,同样内存能多存四倍以上的键。这两个变量功能一样,成本差三倍,多数示例抄的是贵的那一个。

3. 第二段:在 location 里落地

速率定好之后,具体在哪个路径上执行、允许多大的突发,是在 location 里决定的。这是整份配置里唯一需要动脑子的地方。

location /api/ {
    limit_req zone=api_limit burst=10 nodelay;
    limit_req_status 429;
    limit_conn conn_limit 20;

    proxy_pass http://127.0.0.1:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

location = /api/login {
    limit_req zone=login_limit burst=3;
    limit_req_status 429;
    proxy_pass http://127.0.0.1:8000;
}

burst=10 是排队容量:超出速率的请求先压进队列,能等就等。nodelay 的意思是队列里的请求立刻放行,不按速率一个个吐。加了 nodelay,突发流量会被原样打给上游;不加,用户就要等,等久了浏览器自己先超时。

登录接口故意不加 nodelay,队列只留 3 个。这种接口的正确行为是「慢一点也可以,但不能被撞库」,让人等 200 毫秒,比让他试第 4 次密码更划算。同一个站点的不同接口,限流策略本来就该不一样。

4. 第三段:白名单、状态码与日志

限流最怕误伤。监控探针、合作方的回调、办公室出口 IP,都需要单独放过去。做法是把限流的键变成空值——nginx 的规则是「键为空时不计数」。

geo $limit_exempt {
    default        0;
    10.0.0.0/8     1;
    192.168.0.0/16 1;
    203.0.113.7    1;   # 合作方回调出口
}

map $limit_exempt $limit_key {
    0       $binary_remote_addr;
    1       '';
}

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

log_format limited '$remote_addr $request $status req=$limit_req_status';

access_log /var/log/nginx/access.log limited;

$limit_req_status 这个变量很关键,它记录了这次请求是被放行(PASSED)、排队(DELAYED)还是拒绝(REJECTED)。没有这个字段,你只能看到 429 的数量,看不出限流到底在替谁挡枪。

白名单用 IP 段而不是 UA 或 Referer。UA 可以伪造,IP 段不能,代价是运维要维护一份清单。凡是能被客户端随手改掉的特征,都不适合做访问控制的依据。

5. 完整目录与验收

把上面三段拼起来,文件结构是这样:

/etc/nginx/
├── nginx.conf                 # http 块:zone 定义、log_format、include
├── conf.d/
│   └── api.conf               # server 块:location、burst、白名单
└── snippets/
    └── proxy_params           # 可复用的 proxy_set_header

改完先做语法检查,再热加载。这两步顺序不能反,reload 遇到语法错误会保留旧进程,日志里只有一行提示,很容易被当成「生效了」。

sudo nginx -t
sudo systemctl reload nginx
nginx -v

验收用 curl 打 30 次,看 429 出现的节奏。{1..30} 是 bash 的花括号展开,不涉及命令替换,在任何 shell 里都安全。

for i in {1..30}; do
  curl -s -o /dev/null -w '%{http_code}\n' https://example.com/api/health
done | sort | uniq -c

预期结果是二十来个 200 加几个 429,而不是三十个 200,也不是三十个 429。前一种说明限流没生效,后一种说明 burst 太小。限流是否有效,看的是「有没有恰好挡住超出的那部分」,不是「挡得多不多」。

6. 必然踩到的坑

坑一:套了 CDN 之后,所有请求都是同一个 IP。此时 $binary_remote_addr 拿到的是 CDN 节点的地址,全站共用一个桶,第一个用户就能把所有人限死。要先用 set_real_ip_from 声明可信来源,再用 real_ip_header X-Forwarded-For 还原真实 IP,而且 set_real_ip_from 只能写 CDN 的回源段,写 0.0.0.0/0 等于让任何人自带一个假 IP。

坑二:以为 burst 是「允许的并发数」。它是队列长度,配 nodelay 时这十个请求会立刻全部转发给上游。上游只有 2 个 worker,这十个请求照样把它压死。nginx 的限流保护的是 nginx 自己,不保护你的应用。

坑三:改了 rate 却感觉没生效。共享内存里的状态不会因为 reload 清零,旧的计数还在。rate 是编译进 zone 的属性,改完必须重启进程才会按新速率重建。稳妥的做法是换个 zone 名字,旧的自然废弃。

坑四:429 被缓存,或被上游重试放大。如果 429 响应带了较长的 Cache-Control,浏览器会把「你被限流了」缓存下来,用户刷新十次还是 429。另外,配了 proxy_next_upstream 的话,一次超限会变成多次回源。限流响应必须显式声明不缓存,并且从重试名单里排除。

还有一个不算坑但很费时间的点:limit_conn 计的是连接,不是请求。HTTP/1.1 开 keepalive 之后,一条连接上可以跑几十个请求,并发数看起来始终是 1;换成 HTTP/2 之后又是另一套算法。凡是和「连接」有关的指标,都要先确认协议版本再看数。

7. 一份保守的起步参数

不知道自己的站点该配多少,可以从这组数开始,观察一周再调。

  • 普通页面:rate=20r/s、burst=40、nodelay。正常浏览一个页面会带十几个静态请求,速率给低了会误伤。
  • API 接口:rate=5r/s、burst=10、nodelay,前端做好 429 的重试与提示。
  • 登录与验证码:rate=1r/s、burst=3,不加 nodelay,让人等。
  • 下载路径:limit_conn 3、limit_rate 2m,速率和并发一起限。

参数没有通用值。同样 5r/s,对一个内部管理后台是宽松,对一个公开搜索接口是苛刻。能抄的只有结构,速率必须按自己的流量分布算。

写在最后

限流配置写完之后会有一种错觉:好像安全了。实际上它只做了一件事——把「一起死」改成了「按顺序活」。真正打穿带宽的攻击,nginx 拦不住,得靠上游清洗。

但这件事仍然值得做,因为绝大多数事故不是攻击,是一个写错的循环、一个忘了加 sleep 的脚本、一个被分享到群里的链接。运维里最有效的措施,往往不是防住坏人,是拦住自己人。

所以那行 rate=5r/s 的真实含义,不是「你只能来五次」,而是「我们这里一次只能接待这么多,你排一下队」。


你给哪个接口配过限流?当时的数字是拍出来的,还是算出来的?

0

小票为什么会自己变白:一场1950年代的加热,和3种显色剂

上一篇

远程连回家里那台Windows:4个工具的边界,和3种选错的信号

下一篇

评论 (0)

登录后可发布~
立即登录
暂无评论

admin

当生活给你的全是不如意,那你一定要相信自己的兴趣!
225
文章
0
评论
-11
喜欢
2026 年 11 月
一 二 三 四 五 六 日
 1
2345678
9101112131415
16171819202122
23242526272829
30  
« 10 月    

近期评论

聚合文章

当AI生成的图成为呈堂证据:三代人的举证成本,和一个推论
海盗号1976年的那个阳性:48年,和3条没被接受的证据
地铁口贴一张膜,四十分钟,和三次掀起来的膜
AI新闻日报–2026年11月10日:693亿美元,和2倍带宽
洗澡水忽冷忽热:6项措施,按投入时间排的一份清单
远程连回家里那台Windows:4个工具的边界,和3种选错的信号
用nginx 1.24挡住刷接口的爬虫:3段配置,和4个必然踩到的坑
小票为什么会自己变白:一场1950年代的加热,和3种显色剂

猜你喜欢

深入理解 JavaScript 异步编程:从回调到 async/await

深入理解 JavaScript 异步编程:从回调到 async/await

admin
101 0 0
用 nftables 重写 iptables 规则:一份可复现的迁移对照表

用 nftables 重写 iptables 规则:一份可复现的迁移对照表

admin
25 0 0
用 systemd timer 替代 cron:失败重试、日志与错峰执行的完整配置

用 systemd timer 替代 cron:失败重试、日志与错峰执行的完整配置

admin
28 0 0
Python 自动化办公:每天省2小时,从这些脚本开始

Python 自动化办公:每天省2小时,从这些脚本开始

admin
89 0 0

关于我们

你是否对浩瀚的宇宙充满好奇?你是否渴望掌握未来科技的力量?现在,机会来了!“未来星球”将带你进入奇妙世界,开启你的科技探险之旅! 这是一个充满想象和创造力的博客!

分类导航

首页 新闻资讯 技术教程 软件工具 知识百科 生活实用 个人随笔 生命外星人

友情链接

未来星球AI工具【网络版】 未来星球 pycharm官方站【中国】
Copyright © 2026 未来星球. Designed by nicetheme. 蒙ICP备2026002569号-1
  • 请到 [后台->外观->菜单] 中设置菜单
  • AI未来
  • 生活百科
  • 档案
  • 外星人
  • 未来星球

admin

当生活给你的全是不如意,那你一定要相信自己的兴趣!
225
文章
0
评论
-11
喜欢