天天看點

【整理】擷取使用者真實 ip 位址的 nginx 相關配置

nginx 為實作反向代理的需求增加了一個 ngx_http_proxy_module 子產品。其中 proxy_set_header 指令就是該子產品需要讀取的配置。 

      http header 中的 host 含義為所請求的目的主機名。當 nginx 作為反向代理使用,而後端真實 web 伺服器設定有類似 防盜鍊功能 ,或者根據 http header 中的 host 字段來進行 路由 或 過濾 功能的話,若作為反向代理的 nginx 不重寫請求頭中的 host 字段,将會導緻請求失敗。 

      http header 中的  x_forward_for  表示該條 http 請求是由誰發起的。如果反向代理伺服器不重寫該請求頭的話,那麼後端真實 web 伺服器在處理時會認為所有的請求都來自反向代理伺服器。如果後端 web 伺服器有防攻擊政策的話,那麼反向代理伺服器對應的 ip 位址就會被封掉。是以,在配置用作反向代理的 nginx 時,一般會增加以下兩條配置修改 http 的請求頭: 

<a href="http://my.oschina.net/moooofly/blog/295853#">?</a>

1

2

<code>proxy_set_header host $http_host;</code>

<code>proxy_set_header x-forward-for $remote_addr;</code>

      這裡,$http_host 和 $remote_addr 都是 nginx 的導出變量,可以在配置檔案中直接使用。如果 host 沒有出現在 http header 中,則 $http_host 的值為空,而 $host 和 $http_host 同樣表示請求頭中的 host 字段,但若 host 字段不存在,則以實際處理的虛拟主機 server 的 server_name 替代。是以一般而言,會用 $host 代替 $http_host 變量(如下),進而避免 http 請求中丢失 host 頭部的情況下 host 不被重寫的失誤。 

<code>proxy_set_header host $host;</code>

      伺服器叢集之間的通信,是可以信任的。我們要做的就是在離使用者最近的前端代理上,強制設定 x-forward-for 的值,後端所有機器不作任何設定,直接信任并使用前端機器傳遞過來的 x-forward-for 值即可。 

即在最前端的 nginx 上設定: 

3

4

<code>location  ~  ^</code><code>/static</code> <code>{</code>

<code>    </code><code>proxy_pass  ....;</code>

<code>    </code><code>proxy_set_header x-forward-for $remote_addr ;</code>

<code>}</code>

      記住,$remote_addr 是 nginx 的内置變量,代表了用戶端真實(網絡傳輸層)ip 。通過該設定,強行将 x-forward-for 設定為用戶端 ip ,使用戶端無法通過本文所述方式“僞造 ip”。 

      當 nginx 作為反向代理伺服器時,x-forward-for 可以用于把發送者 ip 、代理機器 ip 都記錄下來(用逗号分隔)。 

經過反向代理後,由于在用戶端和 web 伺服器之間增加了中間層,是以 web 伺服器無法直接拿到用戶端的 ip,在 web 伺服器上通過 $remote_addr 變量拿到的将是反向代理伺服器的 ip 位址

      當你使用了 nginx 反向代理後,在 web 端使用類似 request.getremoteaddr() 的 api(本質上就是擷取 $remote_addr),取得的實際上是 nginx 的位址,即 $remote_addr 變量中封裝的是 nginx 的位址,當然是沒法獲得使用者的真實 ip 的,然而 nginx 本身是可以獲得使用者的真實 ip 的,也就是說 nginx 使用 $remote_addr 變量可以獲得使用者的真實 ip ,如果我們想要在 web 端獲得使用者的真實 ip ,就必須在 nginx 這裡作一個指派操作,如下: 

<code>proxy_set_header  x-real-ip $remote_addr;</code>

      其中這個 x-real-ip 是一個自定義的變量名,名字可以随意取,這樣做完之後,使用者的真實 ip 就被放在 x-real-ip 這個變量裡了,然後,在 web 端可以通過類似如下 api 擷取用戶端 ip: 

<code>request.getattribute(</code><code>"x-real-ip"</code><code>)</code>

 x-real-ip 的用途是為了後端 web 伺服器可以擷取使用者的真實 ip , 那麼還要  x-forwarded-for 幹啥? 

      追溯曆史,這個 x-forwarded-for 變量,是 squid 開發的,用于識别請求通過了哪些 http 代理或負載平衡器,記錄相應 ip 位址清單的非 rfc 标準,如果設定了 x-forwarded-for ,那麼每次經過 proxy 轉發請求後都會有記錄,格式就是 

<code>client1, proxy1, proxy2, ...</code>

      以逗号隔開各個位址。由于是非 rfc 标準,是以預設是沒有的,需要強制添加。在預設情況下經過 proxy 轉發的請求,在後端 web 伺服器看來遠端位址都是 proxy 的 ip 位址。也就是說在預設情況下我們使用 request.getattribute("x-forwarded-for") 擷取不到使用者的 ip ,如果我們想要通過這個變量獲得使用者的 ip ,我們需要自己在 nginx 添加如下配置: 

<code>proxy_set_header  x-forwarded-for $proxy_add_x_forwarded_for;</code>

      上述配置的意思是增加一個 $proxy_add_x_forwarded_for 到 x-forwarded-for 裡去,注意是增加,而不是覆寫。當然由于預設的 x-forwarded-for 值是空的,是以我們總感覺 x-forwarded-for 的值就等于 $proxy_add_x_forwarded_for 的值。 

      實際上,當你通過搭建不同 ip 的兩台 nginx 構成多級代理時,并且都使用了這段配置,那你會發現在 web 伺服器端通過類似 request.getattribute("x-forwarded-for") 的 api 獲得的将會是用戶端 ip 和第一台 nginx 的 ip 。 

試驗:【以 http 方式通過 nginx 通路 cowboy】 

浏覽器側抓包 

【整理】擷取使用者真實 ip 位址的 nginx 相關配置

cowboy 上抓包 

【整理】擷取使用者真實 ip 位址的 nginx 相關配置

繼續閱讀