环境:nginx做反向代理,apache做后端服务器 nginx部分配置代码: upstream apache{ server 127.0.0.1:8080; # 后端真实服务器地址及端口 } server { listen 80; server_name www.a.com; root /usr/share/nginx/html; location / { proxy_pass http://apache; proxy_set_header ClientIpGetFromNginx $remote_addr; } 首先先去看一下nginx内置的变量:http://blog.csdn.net/iinel/article/details/4321383 变量 $remote_addr 代表客户端ip地址 通常来说nginx反向代理会添加一个请求头 proxy_set_header X-Forwarded-For $remote_addr; 以此传递客户端ip到后端服务器。 为了方便理解,我这里改一下头的名称: proxy_set_headerClientIpGetFromNginx$remote_addr; 此时用浏览器访问一下,去查看后端服务器的访问日志如下 127.0.0.1 – - [01/Sep/2017:10:31:10 +0800] 后面内容省略··· 可以看到客户端ip为127.0.0.1也就是nginx的ip,(我nginx和后端服务器在一起) 这样一来获取的ip是错误的,那怎样获取正确ip呢? 先来看一下apache日志格式。 LogFormat “%h %l %u %t \”%r\” %>s %b \”%{Referer}i\” \”%{User-Agent}i\”' combined LogFormat “%h %l %u %t \”%r\” %>s %b” common LogFormat “%h %l %u %t \”%r\” %>s %b \”%{Referer}i\” \”%{User-Agent}i\” %I %O” combinedio CustomLog “logs/access_log” combined combined、common、combinedio是apache的3中日志格式,默认用 combined 方式 就需要修改 LogFormat “%h %l %u %t \”%r\” %>s %b \”%{Referer}i\” \”%{User-Agent}i\”' combined 为 LogFormat “%{ClientIpGetFromNginx}i%l %u %t \”%r\” %>s %b \”%{Referer}i\” \”%{User-Agent}i\”' combined 这样一来,就等于从nginx的请求头中取 ClientIpGetFromNginx 变量做为日志开头的ip,即客户端ip (上面i的意思就是从请求头中取ClientIpGetFromNginx,i就代表请求头) 可以参考apache日志格式:http://blog.sina.com.cn/s/blog_672c5a470100xj7z.html 保存配置,再访问一次,再查看apache的访问日志: 192.168.10.105 – - [01/Sep/2017:11:50:41 +0800] 后面内容省略··· 可以看到ip是192.168.10.105,这才是客户端的真实ip。 总结: nginx获取客户端ip是用$remote_addr变量,这个ip是真实的。 后端服务器如果用$remote_addr获取,那么这个ip其实是nginx的ip。 如果nginx设置了传递变量X-Forwarded-For $remote_addr,那么后端用X-Forwarded-For取真实ip 在没有反向代理或CDN的情况下,是不能用X-Forwarded-For获取客户端ip的。因为浏览器是不会发送这个字段的,如果用程序模拟一个访问,这个值是可以被伪造的。 可能会有个想法,如果设置ip传递为 proxy_set_header remote_addr $remote_addr; 是不是就可以不用修改日志格式或者修改代码了? 结果是不行的,会出现一条这样的日志: 127.0.0.1 – - [01/Sep/2017:13:53:42 +0800] 后面内容省略··· ip是127.0.0.1,是nginx的ip地址。表明$remote_addr是不能伪造的。 在上面的设置,通过php中一个数组 $_SERVER 可以获取到: $_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。 通过以下php代码可以看到: <> foreach ($_SERVER as $k => $v) { echo $k . “============” . $v . “ } 保存为1.php,然后去访问这个页面(当然必须有php运行环境)。结果如下 在阿里云官方网站文档中 https://help.aliyun.com/knowledge_detail/40535.html 也可以看到关于ecs获取客户端ip的方法。是一样的。 知识点: 为啥叫 X-Forwarded-For 而不是别的呢? 这是因为当时的squid之类的缓存软件用的比较广,软件官方文档里就用这个名作为标准了。时间一久,大家都遵守这个习惯了。 在标准请求头中是没有这个变量名的。 |
|
来自: Bladexu的文库 > 《技术文摘》