最近网站很不对劲,连续几天打开查看都发现首页底部访客统计代码API请求异常,登陆高德开发平台查看了下状态,说是日免费用量已经耗光了。30000PV呢,不知道被哪个无聊的家伙盯上了,到51啦统计后台查看了下,都是单个IP在短时间内遍历浏览整站网页或者对某几个页面进行大量请求刷新,真是恶心人。
有些请求UA头还变了一下,有些干脆就一个IP、一个UA头咔咔的在这频繁的恶意刷新网页...忍不住吐槽一下,我这一个半公益性质的技术分享类网站,一个广告都没挂呢,就有互联网上各个老六闲不住手了,真是好事是啥都不干,缺德损人的事情那个一干一个上头。
没得办法,通过nginx限制一下吧,小破站这小水管遭不住这样瞎折腾。
目标:希望限制单个 IP 访问 URL 的频率,比如每秒只能请求一次,或每分钟访问一次等,Nginx 的 limit_req_zone 可以实现限制请求速率的需求。
1、了解limit_req_zone:
limit_req_zone命令格式为limit_req_zone $binary_remote_addr zone=itylq:10m rate=1r/m; //zone定义内存区域名称为itylq,内存大小为10MB,约能记录16万个IP地址;rate定义单IP刷新页面频率,上述示例为1次/分钟,也可以定义每秒钟,如rate=1r/s;
根据作用域不同,可以分别配置到http{}、server{}和location{}中:如果放置到http{}域内,则对该nginx下所有网站生效;如果放置在具体网站server{}域内,则只对该网站生效;如果放置到location{}域内,则仅对被匹配上的某个/某类网址生效。
2、部署实践:
**2.1、开启 limit_req_zone 功能:**在http{}域内开启,即需要在nginx上进行配置。
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
limit_req_zone $binary_remote_addr zone=itylq:10m rate=13r/m;
P1.nginx启用limit_req_zone功能
2.2、在server{}域内或者location{}内用对具体网站/URL做具体限制,即调用limit_req:
我目前想要的是对www.itylq.com全站页面的访问频率做一下限制,所以只需要在itylq.com网站的server{}域内调用即可。
server{}域内调用limit_req功能命令格式:limit_req zone=itylq burst=25 nodelay;
//zone调用的是步骤2.1定义的内存区域哈,名称可自定义但需要上下保持一致;burst定义的是并发连接数,即当前请求页面与服务器数据交互的连接数,这个每个站点/页面都是不一样的,一般一张图片就会需要一个并发连接数...
limit_req zone=itylq burst=25 nodelay;
P2.server{}调用limit_req并对单页面并发请求数进行限制
**备注:**burst定义的并发连接数概念并不是传统意义上的同时请求页面数量哈,而是指单个html页面为了从服务器上获取资源需要与之建立的连接数,一般如果某个网页上的图片资源越多,则并发连接数也会越多。每个网站/页面的并发连接数没有定数,需要不断修改测试,直到网站首页和打开网站大部分网页都能顺利加载完全图片等资源,则此时的burst最小值为最优值。当然,不想频繁测试,定义一个100+的数值也是完全ok的,并不影响对单个IP访问网站URL频率的限制。
**总结:**limit_req_zone定义单IP刷新页面频率,并启用limit_req功能;limit_req调用limit_req功能并定义单页面并发连接数。 //单页面并发连接数与单IP访问网站页面的频率无关
扩展: 如果是具体某个页面需要限制单IP的频繁访问,比如防止api接口被滥用或者登陆页面被暴力破解,可以对具体的接口页面或者登陆页面进行访问频率的限制。通过location{}域内调用limit_req来实现:
location = /login {
limit_req zone=itylq burst=25 nodelay;
fastcgi_pass unix:/tmp/php-cgi-73.sock;
#以下固定内容,无需修改
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}
相较于server{}域内调用limit_req,location{}内调用除了limit_req命令外,还需要指定fastcgi_pass路径,另外3行命令无需修改直接复制即可。fastcgi_pass路径可以通过php查询。比如网站使用的是php7.3,则可以通过宝塔面板->软件商店->已安装,找到对应的php,查看FPM配置文件,找到listen字段值复制即可。
P3.通过php设置项FPM配置文件查找fastcgi_pass路径
3、效果测试:
经测试,正常的用户访问没什么影响,当短时间内非正常的、频繁的翻页或者刷新页面时,网站将直接抛出503 Service Temporarily Unavailable错误,基本实现了目标,收工。
P4.单IP短时间内频繁翻页或者刷新页面将被服务器拒绝