现在利用nginx作负载均衡的实例已经不少了,针对不一样的应用场合,还有不少须要注意的地方,本文要说的就是在经过CDN 后到达nginx作负载均衡时请求头中的X-Forwarded-For项到底发生了什么变化。下图为简单的web架构图:php
先来看一下X-Forwarded-For的定义:
X-Forwarded-For:简称XFF头,它表明客户端,也就是HTTP的请求端真实的IP,只有在经过了HTTP 代理或者负载均衡服务器时才会添加该项。它不是RFC中定义的标准请求头信息,在squid缓存代理服务器开发文档中能够找到该项的详细介绍。
标准格式以下:
X-Forwarded-For: client1, proxy1, proxy2
从标准格式能够看出,X-Forwarded-For头信息能够有多个,中间用逗号分隔,第一项为真实的客户端ip,剩下的就是曾经通过的代理或负载均衡的ip地址,通过几个就会出现几个。html
按照上图的Web架构图,能够很容易的看出,当用户请求通过CDN后到达Nginx负载均衡服务器时,其X-Forwarded-For头信息应该为客户端IP,CDN的IP。但实际状况并不是如此,通常状况下CDN服务商为了自身安全考虑会将这个信息作些改动,只保留客户端IP。咱们能够经过php程序得到X-Forwarded-For信息或者经过Nginx的add header方法来设置返回头来查看。java
下面来分析请求头到达Nginx负载均衡服务器的状况;在默认状况下,Nginx并不会对X-Forwarded-For头作任何的处理,除非用户使用proxy_set_header 参数设置:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;nginx
$proxy_add_x_forwarded_for变量包含客户端请求头中的"X-Forwarded-For",与$remote_addr用逗号分开,若是没有"X-Forwarded-For" 请求头,则$proxy_add_x_forwarded_for等于$remote_addr。web
$remote_addr变量的值是客户端的IPapache
当Nginx设置X-Forwarded-For等于$proxy_add_x_forwarded_for后会有两种状况发生后端
一、若是从CDN过来的请求没有设置X-Forwarded-For头(一般这种事情不会发生),而到了咱们这里Nginx设置将其设置为$proxy_add_x_forwarded_for的话,X-Forwarded-For的信息应该为CDN的IP,由于相对于Nginx负载均衡来讲客户端即为CDN,这样的话,后端的web程序时死活也得到不了真实用户的IP的。缓存
二、CDN设置了X-Forwarded-For,咱们这里又设置了一次,且值为$proxy_add_x_forwarded_for的话,那么X-Forwarded-For的内容变成 ”客户端IP,Nginx负载均衡服务器IP“若是是这种状况的话,那后端的程序经过X-Forwarded-For得到客户端IP,则取逗号分隔的第一项便可。安全
如上两点所说,若是咱们知道了CDN设置了X-Forwarded-For信息,且只有客户端真实的IP的话,那么咱们的Nginx负载均衡服务器能够没必要理会该头,让它默认便可。服务器
其实Nginx中还有一个$http_x_forwarded_for变量,这个变量中保存的内容就是请求中的X-Forwarded-For信息。若是后端得到X-Forwarded-For信息的程序兼容性很差的话(没有考虑到X-Forwarded-For含有多个IP的状况),最好就不要将X-Forwarded-For设置为$proxy_add_x_forwarded_for。应该设置为$http_x_forwarded_for或者干脆不设置!
参考文章:http://en.wikipedia.org/wiki/X-Forwarded-For
java获取IP地址的方法:
在个人项目中使用到了apache代理软件,因此最后我通过request.getRemoteAddr()获得都是127.0.0.1,咱们获得的是代理软件的服务器地址。真实的应该是以下:
public String getIpAddr(HttpServletRequest request) {
2 String ip = request.getHeader("x-forwarded-for");
3 if(ip == null || ip.length() == || "unknown".equalsIgnoreCase(ip)) {
4 ip = request.getHeader("Proxy-Client-IP");
5 }
6 if(ip == null || ip.length() == || "unknown".equalsIgnoreCase(ip)) {
7 ip = request.getHeader("WL-Proxy-Client-IP");
8 }
9 if(ip == null || ip.length() == || "unknown".equalsIgnoreCase(ip)) {
10 ip = request.getRemoteAddr();
11 }
12 return ip;
13 }
若是经过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串Ip值,究竟哪一个才是真正的用户端的真实IP呢?
答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
如: X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130, 192.168.1.100 用户真实IP为: 192.168.1.110