Nginx在CDN加速之后,获取用户真实IP做并发访问限制的方法

PHPABC Nginx 1,360 次浏览 , , 没有评论

Nginx 有2个模块用于控制访问“数量”和“速度”,简单的说,控制你最多同时有 多少个访问,并且控制你每秒钟最多访问多少次, 你的同时并发访问不能太多,也不能太快,不然就“杀无赦”。

HttpLimitZoneModule 限制同时并发访问的数量

HttpLimitReqModule 限制访问数据,每秒内最多几个请求

一、普通配置

什么叫普通配置?

普通配置就是针对【用户浏览器】→【网站服务器】这种常规模式的nginx配置。那么,如果我要对单IP做访问限制,绝大多数教程都是这样写的:

## 用户的 IP 地址 $binary_remote_addr 作为 Key,每个 IP 地址最多有 50 个并发连接
## 你想开 几千个连接 刷死我? 超过 50 个连接,直接返回 503 错误给你,根本不处理你的请求了
limit_conn_zone $binary_remote_addr zone=TotalConnLimitZone:10m ;
limit_conn  TotalConnLimitZone  50;
limit_conn_log_level notice;
 
## 用户的 IP 地址 $binary_remote_addr 作为 Key,每个 IP 地址每秒处理 10 个请求
## 你想用程序每秒几百次的刷我,没戏,再快了就不处理了,直接返回 503 错误给你
limit_req_zone $binary_remote_addr zone=ConnLimitZone:10m  rate=10r/s;
limit_req_log_level notice;
 
## 具体服务器配置
server {
	listen   80;
	location ~ \.php$ {
                ## 最多 5 个排队, 由于每秒处理 10 个请求 + 5个排队,你一秒最多发送 15 个请求过来,再多就直接返回 503 错误给你了
		limit_req zone=ConnLimitZone burst=5 nodelay;
		fastcgi_pass   127.0.0.1:9000;
		fastcgi_index  index.php;
		include	fastcgi_params;
	}	
 
}

这样一个最简单的服务器安全限制访问就完成了,这个基本上你 Google 一搜索能搜索到 90% 的网站都是这个例子,而且还强调用“$binary_remote_addr”可以节省内存之类的云云。

二、CDN之后

目前国内已经争相出现了百度云加速、加速乐、360网站卫士以及安全宝等免费CDN。让我们这些小网站也能免费享受以前高大上的CDN加速服务。
于是,网站的访问模式就变为:

用户浏览器 → CDN节点 → 网站源服务器

甚至是更复杂的模式:

用户浏览器 → CDN节点(CDN入口、CC\DDoS攻击流量清洗等) → 阿里云盾 → 源服务器

可以看到,我们的网站中间经历了好几层的透明加速和安全过滤, 这种情况下,我们就不能用上面的“普通配置”。因为普通配置中基于【源IP的限制】的结果就是,我们把【CDN节点】或者【阿里云盾】给限制了,因为这里“源IP”地址不再是真实用户的IP,而是中间CDN节点的IP地址。

我们需要限制的是最前面的真实用户,而不是中间为我们做加速的加速服务器。

其实,当一个 CDN 或者透明代理服务器把用户的请求转到后面服务器的时候,这个 CDN 服务器会在 Http 的头中加入一个记录

X-Forwarded-For : 用户IP, 代理服务器IP

如果中间经历了不止一个代理服务器,这个记录会是这样

X-Forwarded-For : 用户IP, 代理服务器1-IP, 代理服务器2-IP, 代理服务器3-IP, ….

可以看到经过好多层代理之后, 用户的真实IP 在第一个位置, 后面会跟一串中间代理服务器的IP地址,从这里取到用户真实的IP地址,针对这个 IP 地址做限制就可以了。
那么针对CDN模式下的访问限制配置就应该这样写:

## 这里取得原始用户的IP地址
map $http_x_forwarded_for  $clientRealIp {
	""	$remote_addr;
	~^(?P<firstAddr>[0-9\.]+),?.*$	$firstAddr;
}
 
## 针对原始用户 IP 地址做限制
limit_conn_zone $clientRealIp zone=TotalConnLimitZone:20m ;
limit_conn  TotalConnLimitZone  50;
limit_conn_log_level notice;
 
## 针对原始用户 IP 地址做限制
limit_req_zone $clientRealIp zone=ConnLimitZone:20m  rate=10r/s;
#limit_req zone=ConnLimitZone burst=10 nodelay; #如果开启此条规则,burst=10的限制将会在nginx全局生效
limit_req_log_level notice;
 
## 具体Server:如下在监听php部分新增限制规则即可
server {
	listen   80;
	location ~ \.php$ {
                ## 最多 5 个排队, 由于每秒处理 10 个请求 + 5个排队,你一秒最多发送 15 个请求过来,再多就直接返回 503 错误给你了
		limit_req zone=ConnLimitZone burst=5 nodelay;
 
		fastcgi_pass   127.0.0.1:9000;
		fastcgi_index  index.php;
		include	fastcgi_params;
	}	
 
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

Go