Nginx配置文件詳解2
1、proxy_set_header 重新定義或者添加轉發的請求頭
proxy_set_header 的語法:
# 語法。在http.server.location中設置
proxy_set_header field value;
#默認值
proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
proxy_set_header 允許重新定義或者添加發往后端服務器的請求頭。value 可以包含文本、變量或者它們的組合。 當且僅當當前配置級別中沒有定義 proxy_set_header 指令時,會從上面的級別繼承配置。 proxy_set_header 就是可設置請求頭,并將頭信息傳遞到服務器端。不屬于請求頭的參數中也需要傳遞時重定義下就行啦。
在 java 端可以使用 request.getHeader(field) 方法來獲取 獲取 proxy_set_header 設置的參數的值,一般在用來獲取真實 ip 地址時會用到。
2、獲取用戶真實ip
2.1、X-Forwarded-For 獲取用戶真實ip
X-Forwarded-For 是一個 HTTP 擴展頭部。HTTP/1.1(RFC 2616)協議并沒有對它的定義,它最開始是由 Squid 這個緩存代理軟件引入,用來表示 HTTP 請求端真實 IP。如今它已經成為事實上的標準,被各大 HTTP 代理、負載均衡等轉發服務廣泛使用,并被寫入 RFC 7239(Forwarded HTTP Extension)標準之中。
由于它是非 rfc 標準,所以默認情況下是沒有該請求頭的,即 X-Forwarded-For 的值在默認情況下是空的。現在我們需要利用該請求頭來記錄用戶的真實 ip,此時需要我們手動在nginx添加配置:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
X-Forwarded-For 請求頭的最后顯示的格式:
X-Forwarded-For: client, proxy1, proxy2
可以看到,XFF 的內容由逗號 , 隔開的多個部分組成,最開始的是離服務端最遠的設備 IP,然后是每一級代理設備的 IP。
假設一個 HTTP 請求到達服務器之前,經過了三個代理 Proxy1、Proxy2、Proxy3,IP 分別為 IP1、IP2、IP3,用戶真實 IP 為 IP0,即:

那么按照 XFF 標準,服務端最終會收到以下信息:
X-Forwarded-For: IP0, IP1, IP2
Proxy3 直連服務器,它會給 XFF 追加 IP2,表示它是在幫 Proxy2 轉發請求。(注意,列表中并沒有 IP3,因為$proxy_add_x_forwarded_for 變量只能獲取到請求者的ip,當前接收者的ip不會插入進去。所以在 Proxy3 轉發給最終的接收者 server 時,server 接收到的請求 X-Forwarded-For 并不會包含 IP3。當然,IP3 可以在 server 服務端通過 Remote Address 字段獲得。不同語言獲取 Remote Address 的方式不一樣,例如 php 是 $_SERVER["REMOTE_ADDR"],Node.js 是 req.connection.remoteAddress,但原理都一樣。)
假設架構如下:

如果在 128 代理服務器上做以下配置:
upstream nginxServer {
hash $http_x_forwarded_for; #這里是為了會話保持
server 192.168.32.130:80;
server 192.168.32.130:81;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://nginxServer;
}
}
注意,上面并沒有做 X-Forwarded-For 的配置。(我們在 128 和兩臺 130 服務器上修改 Nginx 日志輸出格式如下,這樣就可以通過日志輸出的 $http_x_forwarded_for 看到 X-Forwarded-For 請求頭的值。)
http {
...
log_format main '$http_x_forwarded_for|$http_x_real_ip|$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main; #注意,必須添加這句,沒有這句上面的配置不會起作用
...
}
128 服務器的日志:

130 服務器的日志:

可以看到,在 128 和 130 上都沒有 X-Forwarded-For 請求頭的數據,也就說明在默認情況下是沒有 X-Forwarded-For 請求頭的,我們需要主動在 Nginx 上配置。
由此我們在 128 服務器上添加配置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for,最終配置如下:
upstream nginxServer {
hash $http_x_forwarded_for;
server 192.168.32.130:80;
server 192.168.32.130:81;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://nginxServer;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #添加了X-Forwarded-For配置
}
}
重啟服務器發出請求再次查看日志如下:
128 服務器日志:

130 服務器日志:

可以看到,在 128 服務器上仍然沒有 X-Forwarded-For 請求頭的數據(這是正常的),但在 130 服務器可以看到 X-Forwarded-For 請求頭的數據,指向的確實是用戶的真實 ip。如果經過多個代理,你會發現 X-Forwarded-For 請求頭是 client, proxy1, proxy2 的格式。
參考:https://blog.csdn.net/qq_34556414/article/details/78185057
2.2、利用 $remote_addr 獲取用戶真實ip
在實際應用中,我們可能需要獲取用戶的ip地址,比如統計 ip 訪問次數等。通常情況下我們使用 request.getRemoteAddr() 就可以獲取到客戶端ip,但是當我們使用了nginx作為反向代理后,直接使用 request.getRemoteAddr() 獲取到的就一直是 nginx 服務器的 ip 的地址,那這時應該怎么辦?
比如架構如下:

如果我們在 130 的服務器上直接通過 request.getRemoteAddr() 獲取 ip 地址,實際上獲取到的是 128 Nginx 服務器的 ip,而不是用戶的 32.1 的ip。在 130 的兩臺服務器上獲取不到用戶的真實 ip,但實際上在 128 Nginx 服務器上是可以通過 $remote_addr 變量獲取到用戶的真實 ip 的。
我們可以在 128 代理服務器上做以下配置(上面架構只需在 128 配置 x-real-ip,130無需配置):
upstream nginxServer { hash $http_x_forwarded_for; #這里是為了會話保持 server 192.168.32.130:80; server 192.168.32.130:81; } server { listen 80; server_name localhost; location / { proxy_pass http://nginxServer; proxy_set_header Connection ""; proxy_set_header X-Real-IP $remote_addr; } }
其實 X-Real-Ip 只是一個自定義的變量名,X-Real-Ip目前并不屬于任何標準,代理和 Web 應用之間可以約定用任何自定義頭來傳遞這個信息。上面配置完,用戶的真實 ip 就被放在 X-Real-IP 這個變量里了,然后在web端可以這樣獲取:request.getAttribute("X-Real-IP")。
我們在 128 和兩臺 130 服務器上修改 Nginx 日志輸出格式如下,可以通過日志輸出來看到 130 服務器上的 $http_x_real_ip 有輸出用戶的真實 ip:
http { ... log_format main '$http_x_forwarded_for|$http_x_real_ip|$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; #注意,必須添加這句,沒有這句上面的配置不會起作用 ... }
128 服務器的日志如下, 可以看到,$remote_addr 變量輸出了用戶的真實 ip 地址。

130 服務器的日志如下,可以看到,在 $http_x_real_ip 變量中可以看到用戶真實的 ip 地址,并且 $remote_addr 指向的是 128 代理服務器的地址。

上面適用的是只經過了一個代理服務器,如果經過多個代理服務器時,$remote_addr 指的將是上一個代理服務器的 ip 而不是用戶的 ip,此時可以像下面這么做。
#在第一個代理服務器中設置 set x_real_ip=$remote_addr #最后一個代理服務器中獲取 $x_real_ip=IP1
參考:https://blog.csdn.net/qq_34556414/article/details/78185057
3、upstream
upstream rtpserver {
#server 192.168.32.128:8081;
server 192.168.32.128:8082;
check_interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "GET / HTTP/1.1\r\nConnection:keep-alive\r\n\r\n";
check_http_expect_alive http_2xx http_3xx http_4xx;
keepalive 100;
}
指令:
Syntax: check
interval=milliseconds [fall=count] [rise=count] [timeout=milliseconds] [default_down=true|false] [type=tcp|http|ssl_hello|mysql|ajp] [port=check_port]
Default: 如果沒有配置參數,默認值是:interval=30000 fall=5 rise=2 timeout=1000 default_down=true type=tcp
Context:upstream
該指令可以打開后端服務器的健康檢查功能。
指令后面的參數意義是:
interval:向后端發送的健康檢查包的間隔。fall(fall_count): 如果連續失敗次數達到fall_count,服務器就被認為是down。rise(rise_count): 如果連續成功次數達到rise_count,服務器就被認為是up。timeout: 后端健康請求的超時時間。default_down: 設定初始時服務器的狀態,如果是true,就說明默認是down的,如果是false,就是up的。默認值是true,也就是一開始服務器認為是不可用,要等健康檢查包達到一定成功次數以后才會被認為是健康的。type:健康檢查包的類型,現在支持以下多種類型tcp:簡單的tcp連接,如果連接成功,就說明后端正常。ssl_hello:發送一個初始的SSL hello包并接受服務器的SSL hello包。http:發送HTTP請求,通過后端的回復包的狀態來判斷后端是否存活。mysql: 向mysql服務器連接,通過接收服務器的greeting包來判斷后端是否存活。ajp:向后端發送AJP協議的Cping包,通過接收Cpong包來判斷后端是否存活。
port: 指定后端服務器的檢查端口。你可以指定不同于真實服務的后端服務器的端口,比如后端提供的是443端口的應用,你可以去檢查80端口的狀態來判斷后端健康狀況。默認是0,表示跟后端server提供真實服務的端口一樣。該選項出現于Tengine-1.4.0。
Syntax: check_http_send
http_packet
Default:"GET / HTTP/1.0\r\n\r\n"
Context:upstream
該指令可以配置http健康檢查包發送的請求內容。為了減少傳輸數據量,推薦采用"HEAD"方法。
當采用長連接進行健康檢查時,需在該指令中添加keep-alive請求頭,如:"HEAD / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"。
同時,在采用"GET"方法的情況下,請求uri的size不宜過大,確保可以在1個interval內傳輸完成,否則會被健康檢查模塊視為后端服務器或網絡異常。
Syntax: check_http_expect_alive
[ http_2xx | http_3xx | http_4xx | http_5xx ]
Default:http_2xx | http_3xx
Context:upstream
該指令指定HTTP回復的成功狀態,默認認為2XX和3XX的狀態是健康的。
參考:https://tengine.taobao.org/document_cn/http_upstream_check_cn.html

浙公網安備 33010602011771號