在保护 API 接口、阻止暴力破解和预防 CC 攻击等方面,我们希望限制单个 IP 访问 URL 的频率,比如每秒只能请求一次,或每分钟访问一次等,Nginx 的 limit_req_zone
可以实现限制请求速率的需求。虫子菌将以宝塔面板 + WordPress 网站为例,详细解读 limit_req_zone 的参数配置。
1. limit_req_zone
http { #... #设置名为 cccitu 的内存区域,10 MB大小,单个 IP 限制每分钟访问 1 次 limit_req_zone $binary_remote_addr zone=cccitu:10m rate=1r/m; server { #... #对 https://www.cccitu.com/5065.html 进行访问频率限制 location =/5065.html { #如果同 IP 一分钟内发起 10 并发请求,第 1 个请求立即处理,缓存 5 个请求后每分钟处理一个,剩下的 4 个直接返回 503 错误 limit_req zone=cccitu burst=5 nodelay; } } }
如上所示,limit_req_zone 的启用只有非常简单的两行代码,将它们写在 Nginx 的配置文件中即可生效。
imit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
只能放在 http {} 内;limit_req zone=one burst=5 nodelay;
则可以根据需要放在 http {} (对服务器内所有的网站生效)、server {} (对具体的一个网站生效)或 location {} (对具体的一个网址生效)
2. 实例部署
虫子菌以使用宝塔面板部署的 WordPress 的网站为例,假设要限制对 https://www.cccitu.com/5065.html
的请求速率,每个 IP 地址,每分钟只能访问此网址 1 次。部署过程分为两部分:第一部分在 http {} 内开启 limit_req_zone 功能,第二部分在 location {} 内对具体 URL 做频率限制。
2.1 第一部分 http {}
2.1.1 代码部署:
#zone= 和 rate= 的内容,可以根据需要自行设置 limit_req_zone $binary_remote_addr zone=cccitu:10m rate=1r/s;
将如上代码添加到 Nginx 配置文件的 http {} 内。
2.1.2 参数解读:
zone=cccitu:10m
:将 IP 地址保存在名为 cccitu 内存区域,该区域储存空间为 10 MB。区域名称可以随便设置,但要注意后面用的时候名称要一致;1m 空间大约能保存 1.6 万条 IP 地址,10m 完全够用了,空间满了新数据会覆盖旧数据。
rate=1r/m
: 限制访问请求频率为每分钟 1 次,可根据需要自行设置,1r/s
是 1 秒 1 次,30r/m
是 1 分钟 30 次(换算为 2 秒 1 次),需要注意时间单位只能选择 s (秒)或 m (分),最低频率限制是每分钟 1 次访问请求。
2.2 第二部分 location {}
2.2.1 代码部署:
location = /5065.html { #以下需根据实际情况修改 #教程:https://www.cccitu.com/5065.html limit_req zone=cccitu burst=5 nodelay; try_files $uri $uri/ /index.php?$args; fastcgi_pass unix:/tmp/php-cgi-73.sock; #以下固定内容,无需修改 fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; }
将如上代码添加到 Nginx 的配置文件针对 www.cccitu.com 站点的 server {} 内。
2.2.2 参数解读:
location = /5065.html
:= /5065.html 只对网址 www.cccitu.com/5065.html 生效,= / 只对 www.cccitu.com 生效,只保留 location 后面什么也不写则是对该网站的所有网址生效。
limit_req zone=cccitu burst=5 nodelay;
:zone=cccitu 是存放 IP 地址的内存区域名称,要与第一部分在 http {} 中设置的名称保持,前面叫 cccitu,这里也要写 cccitu。这一行其实有 3 种写法,在此例子中我们将限制频率为 1 分钟 1 次(rate=1r/s),如果瞬间并发了 10 次请求,这 3 种写法的效果分别如下 :
#立即处理第 1 个请求,其余 9 个直接返回 503 错误 limit_req zone=cccitu; #立即处理第 1 个请求,第 2-6 个请求缓存后排队 1 分钟处理 1 个,第 7-10 个请求返回 503 错误 limit_req zone=cccitu burst=5; #立即处理第 1-5 个请求,第 7-10 请求返回 503 错误 limit_req zone=cccitu burst=5 nodelay;
try_files $uri $uri/ /index.php?$args;
:是 WordPress 的伪静态(宝塔面板——网站——设置——伪静态)如果你没设置伪静态或者没用 WordPress,可以删掉不写。
fastcgi_pass unix:/tmp/php-cgi.sock;
:PHP 连接配置,有 192.168.1.25:9001
或 unix:/tmp/php8.sock
两种写法,并且不同版本的 PHP 所写内容也不同,具体怎么写,请与所使用 PHP 的 FPM 配置文件一致(宝塔面板——软件商店——所安装PHP——FPM 配置文件)
其它没提及的内容,是默认固定配置,不用专门修改。
2.3 额外补充:
location = /5065.html { #教程:https://www.cccitu.com/5065.html limit_req zone=cccitu burst=5 nodelay; try_files $uri $uri/ /index.php?$args; }
对于宝塔面板 + WordPress,如果针对的 URL 是文章/标签/分类,不是 网站首页、wp-login.php、admin-ajax.php 等,那么,http {} 保持不变,location {} 有更简单的写法,如上所示。
写的最通俗易懂的,并且按照教学设置好了!
但是我有个问题想问下大佬,希望大佬看到了能给我一个回复~
就是rate=2r/s; 这个请求是什么意思?
是指一个页面吗?还是说这个页面上所有的内容,包括CSS,JS,图片等等~
我设置了2r/s,但是页面也能打开~
然后就是burst,我设置了burst=1,似乎一些图片什么的就显示不出来了~
我就有点搞不明白这个请求~~
可以讲解一下吗?
@7042-22120219 如果不是针对某个具体的URL做限制,那么:
rate=2r/s 是每秒允许2个请求,网页由 html/css/js/png/jpg 等组成。如果你网站没用 CDN,所有 JS/CSS/PNG 等文件都保存在服务器并且每次都从服务器读取,那么别人打开你的一个网址,就要同时产生 html/css/js/png 等 10几个请求,如果只允许每秒 2 次请求,就非常严苛了。具体的请求次数,你可以在浏览器控制台查看。
burst=1 是允许的列队数量。假如一个页面有 10 次请求,你设置 burst=1 ,就是允许在第1秒处理其中的 2 个请求,剩下的8个请求,在第 2 秒处理 1 个,剩下的 7 个请求返回 503 错误,所以你网页的的图片请求,被报 503,就打不开了。
@CCCiTU 我看浏览器下面请求要将近100次,有的页面要将近200个请求了。。如果设置1秒允许一个ip访问1个html页面。。我设置200r/s,burst=50,应该可以吧?
@7042-22120219 100次全是来自你服务器的域名吗?如果你用了广告联盟/或者其它第三方JS,那么真实的请求数量就不会这么多了,其次css/js一般会缓存到浏览器,除了第一次访问以及缓存到期了,一般不会反复请求。这些情况你需要综合考虑。如果你只是想控制每个IP的并发访问次数,而不是类似1小时只允许一个IP访问一千次这种固定的次数限制,那么 burst 可以设置高一些没事,让这些请求1秒1秒的排队处理就行,用户访问会慢,但不会报错。
@CCCiTU 并发不是一个ip同时打开很多页面才叫并发吗?我只是想限制单个ip的访问速率,我觉得我网站有一些ip每天访问太高了,估计是抓取或者是采集的,这些到无所谓,但是有时加上自然访问高峰,服务器的负载和cpu就直接百分百了。所以想限制下速率。。单个ip控制在1秒一个页面,毕竟正常访问,打开一个页面就在1秒左右。。
@7042-22120219 只控制并发的话,那你预估一下页面请求次数,来控制允许每秒或者每分钟的访问次数就行了。