Nginx proxy_set_header:即允許重新定義或新增欄位傳遞給代理伺服器的請求頭。該值可以包含文字、變數和它們的組合。在沒有定義proxy_set_header時會繼承之前定義的值。預設情況下,只有兩個欄位被重定義:
proxy_set_header Host $proxy_host; proxy_set_header Connection close;
如果啟用快取,來自之前請求的頭欄位“If-Modified-Since”, “If-Unmodified-Since”, “If-None-Match”, “If-Match”, “Range”, 和 “If-Range” 將不會被代理伺服器傳遞。
一個不會變化的“Host”頭請求欄位可通過如下方式被傳遞:
proxy_set_header Host $http_host;
然後,當欄位不在請求頭中就無法傳遞了,在這種情況下,可通過設定Host變數,將需傳遞值賦給Host變數
proxy_set_header Host $host;
此外,伺服器名稱和埠一起通過代理伺服器傳遞
proxy_set_header Host $host:$proxy_port;
如果請求頭的存在空的欄位將不會通過代理伺服器傳遞出去
proxy_set_header Accept-Encoding "";
簡而言之,proxy_set_header 就是可設定請求頭-並將頭資訊傳遞到伺服器端,不屬於請求頭的引數中也需要傳遞時,重定義下即可!
================================接下來看下測試案例=========================
1)如下測試,不設定 proxy_set_header Nginx 配置: upstream test { server 192.168.1.123:9099; server 192.168.1.123:58080; } server { listen 5800; server_name 192.168.1.123; root /usr/share/nginx/html; include /etc/nginx/default.d/*.conf; location / { proxy_pass http://test; } 測試jsp 想獲取客戶端IP、客戶端port、代理伺服器IP、代理伺服器port <%@page contentType="text/html; charset=UTF-8" trimDirectiveWhitespaces="true"%> <% String scheme = request.getScheme(); String serverName = request.getServerName(); String remoteName = request.getRemoteAddr(); String realIP = request.getHeader("X-Forwarded-For"); String realIP2 = request.getHeader("X-Real-IP"); String Host = request.getHeader("Host"); int port = request.getServerPort(); int portR = request.getRemotePort(); String requestURIC1 = scheme+"://"+realIP+":"+portR; String requestURIC2 = scheme+"://"+realIP2+":"+portR; String requestURIC3 = scheme+"://"+remoteName+":"+portR; String requestURI = scheme+"://"+serverName+":"+port; %> 其中: 客戶端地址1:<%=requestURIC1 %> 客戶端地址2:<%=requestURIC2 %> 客戶端地址3:<%=requestURIC3%> 伺服器地址1:<%=requestURI%> 伺服器地址2:<%=Host%> 測試結果 客戶端地址1:http://null:58828 客戶端地址2:http://null:58828 客戶端地址3:http://192.168.1.123:58828 伺服器地址1:http://test:80 伺服器地址2:test Nginx日誌 192.168.1.177 -20508---5800 [25/Aug/2016:16:34:13 +0800] "GET /docs/test.jsp HTTP/1.1" 200 223 " 其中客戶端IP不能獲取到,而通過request.getRemoteAddr(); 獲取的IP是代理伺服器IP,而不是客戶端IP,而在nginx中$remote_addr變數的值是客戶端的IP,可見remoteaddr沒有傳遞。 而server_port值也不對,當前值為5800,當前列印出的是80。 而當前代理為http://test 所有通過host得到的是test。 客戶端port也獲取不到值為20508,可傳給應用的是58828 ---------------------------------------------------------------------------------------------------------------- 2)如下測試,設定 proxy_set_header Nginx 配置: upstream test { server 192.168.1.123:9099; server 192.168.1.123:58080; } server { listen 5800; server_name 192.168.1.123; root /usr/share/nginx/html; include /etc/nginx/default.d/*.conf; location / { proxy_pass http://test; proxy_set_header Host $host:$server_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-PORT $remote_port; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } 測試頁面改成: <%@page contentType="text/html; charset=UTF-8" trimDirectiveWhitespaces="true"%> <% String scheme = request.getScheme(); String serverName = request.getServerName(); String remoteName = request.getRemoteAddr(); String realIP = request.getHeader("X-Forwarded-For"); String realIP2 = request.getHeader("X-Real-IP"); String Host = request.getHeader("Host"); int port = request.getServerPort(); int portR = request.getRemotePort(); String portR2 = request.getHeader("X-Real-Port"); String requestURIC1 = scheme+"://"+realIP+":"+portR; String requestURIC2 = scheme+"://"+realIP2+":"+portR; String requestURIC3 = scheme+"://"+remoteName+":"+portR; String requestURI = scheme+"://"+serverName+":"+port; %> 其中: 客戶端地址1:<%=requestURIC1 %> 客戶端地址2:<%=requestURIC2 %> 客戶端地址3:<%=requestURIC3%> 伺服器地址1:<%=requestURI%> 伺服器地址2:<%=Host%> 客戶端port2:<%=portR2%> 客戶端地址1:http://192.168.1.177:21548 客戶端地址2:http://192.168.1.177:21548 客戶端地址3:http://192.168.1.123:21548 伺服器地址1:http://192.168.1.123:5800 伺服器地址2:192.168.1.123:5800 客戶端port2:20604 nginx日誌: 192.168.1.177 -20604---5800 [25/Aug/2016:16:38:42 +0800] "GET /docs/test.jsp HTTP/1.1" 200 275 "-" "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36" "-" 除request.getRemoteAddr();獲取的值不對外,其他值都是對的。 getRemoteAddr獲取的是代理的請求地址。 因重定義了host,所以test值被改寫成代理伺服器IP。 因重定義了 X-Real-PORT-並傳遞$remote_port,客戶端port也獲取正確了。
======================proxy_set_header自定義header頭無效的問題========================
nginx反向代理中經常碰過的一個"坑":proxy_set_header自定義header頭無效的問題 解決辦法: nginx underscores_in_headers預設off 可以用減號-替代下劃線符號_,避免這種變態問題。nginx預設忽略掉下劃線可能有些原因。 upstream os-8080 { ip_hash; server 192.168.1.20:8080 max_fails=3 fail_timeout=15s; server 192.168.1.21:8080 max_fails=3 fail_timeout=15s; } server { listen 80; server_name bpm.wangshibo.com; access_log /data/nginx/logs/bpm.wangshibo.com-access.log main; error_log /data/nginx/logs/bpm.wangshibo.com-error.log; nginx underscores_in_headers on; location / { proxy_pass http://os-8080; proxy_redirect off ; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 300; proxy_send_timeout 300; proxy_read_timeout 600; proxy_buffer_size 512k; proxy_buffers 8 512k; proxy_busy_buffers_size 512k; proxy_temp_file_write_size 512k; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404; proxy_max_temp_file_size 128m; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } }
================proxy_set_header中$proxy_host,$host,$http_host的區別================
在使用Nginx做反向代理的時候,proxy_set_header功能可以設定反向代理後的http header中的host,$http_host,$proxy_host,那麼這幾個有什麼區別呢? Nginx的官網文件中說下面這兩條是做反代時預設的,所以$proxy_host 自然是 proxy_pass後面跟著的host了 proxy_set_header Host $proxy_host; proxy_set_header Connection close; 如果客戶端發過來的請求的header中有’HOST’這個欄位時, $http_host和$host都是原始的’HOST’欄位 比如請求的時候HOST的值是www.csdn.net 那麼反代後還是www.csdn.net 如果客戶端發過來的請求的header中沒有有’HOST’這個欄位時, 建議使用$host,這表示請求中的server name。
==================不妨看一個proxy_set_header配置例項==================
windows客戶端(請求web服務):192.168.1.1 nginx作為反向代理伺服器:192.168.1.136 nginx作為後端web伺服器:192.168.1.137 前提條件:配置nginx轉發到後端伺服器 server { listen 8080; server_name 192.168.1.136; location / { root "/www/html"; index index.html; #auth_basic "required auth"; #auth_basic_user_file "/usr/local/nginx/users/.htpasswd"; error_page 404 /404.html; } location /images/ { root "/www"; rewrite ^/images/bbs/(.*\.jpeg)$ /images/$1 break; rewrite ^/images/www/(.*)$ http://192.168.1.136/$1 redirect; } location /basic_status { stub_status; } location ^~/proxy_path/ { root "/www/html"; index index.html; proxy_pass http://192.168.1.137/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; #proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location ^~/proxy_path/ { root "/www/html"; index index.html; proxy_pass http://192.168.1.137/; } } 將左側匹配到的/proxy_path/開頭的url全部轉發到後端伺服器192.168.223.137
下面將一一測試各個proxy_set_header設定的變數的內容:
1)proxy_set_header Host $host; 將136代理伺服器,137後端伺服器的log_format修改為如下: log_format main '$remote_addr - $remote_user [$time_local] "$request" $http_host ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; proxy_set_header Host $host; 這裡的Host變數的值對應的就是日誌中的$http_host 的值 當windows使用者訪問http://192.168.1.136:8080/proxy_path/index.html時 檢視代理伺服器和後端伺服器的地址,可以發現$http_host對應的值為192.168.1.136:8080 192.168.1.1 - - [18/Jul/2017:10:21:25 +0800] "GET /favicon.ico HTTP/1.1" 192.168.1.136:8080 404 24 "http://192.168.1.136:8080/proxy_path/index.html" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"
如果將後端伺服器關閉了,則會出現502網管錯誤:
然後開啟137後端nginx,檢視日誌:
192.168.1.136 "192.168.1.1" - - [17/Jul/2017:17:06:44 +0800] "GET /index.html HTTP/1.0" "192.168.1.136" 304 0 "-" "Mozilla/5.0
(Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "192.168.1.1"
即驗證了proxy_set_header Host $host; $host就是nginx代理伺服器,也就是windows客戶端請求的host
2)proxy_set_header Host $proxy_host; 將設定修改為上述proxy_host然後重啟ngxin代理伺服器136 [root@wadeson nginx]# sbin/nginx -s reload 重新請求代理頁面:http://192.168.1.136:8080/proxy_path/index.html,然後日誌如下: 首先檢視136代理伺服器的日誌: 192.168.1.1 - - [18/Jul/2017:10:30:12 +0800] "GET /proxy_path/index.html HTTP/1.1" 192.168.1.136:8080 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-" 因為windows是136的客戶端,請求的host為192.168.223.136:8080,而nginx代理伺服器作為137後端伺服器的客戶端,將請求的報文首部重新封裝, 將proxy_host封裝為請求的host 那麼137上面日誌請求的host就是其自身,proxy_host就是代理伺服器請求的host也就是後端伺服器137 192.168.1.136 "192.168.1.1" - - [18/Jul/2017:10:30:12 +0800] "GET /index.html HTTP/1.0" "192.168.1.137" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "192.168.1.1" 3)proxy_set_header Host $host:$proxy_port; 瞭解了上面的知識,那麼此處對應的host就知道代表的啥了,$host代表轉發伺服器,$proxy_port代表136轉發伺服器請求後端伺服器的埠,也就是80。 於是觀察136、137的日誌進行驗證: 192.168.1.1 - - [18/Jul/2017:10:38:38 +0800] "GET /proxy_path/index.html HTTP/1.1" 192.168.1.136:8080 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-" 192.168.1.136 "192.168.1.1" - - [18/Jul/2017:10:38:38 +0800] "GET /index.html HTTP/1.0" "192.168.1.136:80" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "192.168.1.1" 4)proxy_set_header X-Real-IP $remote_addr; 將$remote_addr的值放進變數X-Real-IP中,此變數名可變,$remote_addr的值為客戶端的ip nginx轉發136伺服器日誌格式為: log_format main '$remote_addr - $remote_user [$time_local] "$request" $http_host ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; nginx後端137伺服器的日誌格式: log_format main '$remote_addr "$http_x_real_ip" - $remote_user [$time_local] "$request" "$http_host" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; 兩者區別在於"$http_x_real_ip",新增了這個變數的值 重新請求需要訪問的地址http://192.168.1.136:8080/proxy_path/index.html 136的日誌: 192.168.1.1 - - [18/Jul/2017:10:45:07 +0800] "GET /proxy_path/index.html HTTP/1.1" 192.168.1.136:8080 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-" 137的日誌: 192.168.1.136 "192.168.1.1" - - [18/Jul/2017:10:45:07 +0800] "GET /index.html HTTP/1.0" "192.168.1.136:80" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "192.168.1.1" 紅色標記的就是"$http_x_real_ip"的值,即可以看見使用者真實的ip,也就是客戶端的真實ip 5)proxy_set_header X-Forwarded-For $remote_addr; 理解了上面的含義那麼這個封裝報文的意思也就請求了 首先還是比對136和137的日誌格式: 136代理伺服器的日誌格式: log_format main '$remote_addr - $remote_user [$time_local] "$request" $http_host ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; 137後端伺服器的日誌格式: log_format main '$remote_addr "$http_x_real_ip" - $remote_user [$time_local] "$request" "$http_host" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; 重新請求需要訪問的地址http://192.168.1.136:8080/proxy_path/index.html 136的日誌顯示: 192.168.1.1 - - [18/Jul/2017:10:51:25 +0800] "GET /proxy_path/index.html HTTP/1.1" 192.168.1.136:8080 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-",最後一個欄位 "$http_x_forwarded_for"對應的為空值 137的日誌顯示: 192.168.1.136 "192.168.1.1" - - [18/Jul/2017:10:51:25 +0800] "GET /index.html HTTP/1.0" "192.168.1.136:80" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "192.168.1.1" 可以看出137後端伺服器成功的顯示了真實客戶端的ip 6)proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 5、6兩者的區別: 在只有一個代理伺服器的轉發的情況下,兩者的效果貌似差不多,都可以真實的顯示出客戶端原始ip 但是區別在於: $proxy_add_x_forwarded_for變數包含客戶端請求頭中的"X-Forwarded-For",與$remote_addr兩部分,他們之間用逗號分開。 -------------------------------------------------------------------------------------------------------- 舉個例子,有一個web應用,在它之前通過了兩個nginx轉發,www.linuxidc.com 即使用者訪問該web通過兩臺nginx。 在第一臺nginx中,使用 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 現在的$proxy_add_x_forwarded_for變數的"X-Forwarded-For"部分是空的,所以只有$remote_addr,而$remote_addr的值是使用者的ip,於是賦值以後, X-Forwarded-For變數的值就是使用者的真實的ip地址了。 到了第二臺nginx,使用 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 現在的$proxy_add_x_forwarded_for變數,X-Forwarded-For部分包含的是使用者的真實ip,$remote_addr部分的值是上一臺nginx的ip地址, 於是通過這個賦值以後現在的X-Forwarded-For的值就變成了“使用者的真實ip,第一臺nginx的ip”,這樣就清楚了吧。