現在利用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