Nginx proxy_set_header:即容許從新定義或添加字段傳遞給代理服務器的請求頭。該值能夠包含文本、變量和它們的組合。在沒有定義proxy_set_header時會繼承以前定義的值。默認狀況下,只有兩個字段被重定義:html
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」頭請求字段可經過以下方式被傳遞:nginx
proxy_set_header Host $http_host;
而後,當字段不在請求頭中就沒法傳遞了,在這種狀況下,可經過設置Host變量,將需傳遞值賦給Host變量web
proxy_set_header Host $host;
此外,服務器名稱和端口一塊兒經過代理服務器傳遞windows
proxy_set_header Host $host:$proxy_port;
若是請求頭的存在空的字段將不會經過代理服務器傳遞出去後端
proxy_set_header Accept-Encoding "";
簡而言之,proxy_set_header 就是可設置請求頭-並將頭信息傳遞到服務器端,不屬於請求頭的參數中也須要傳遞時,重定義下便可!緩存
================================接下來看下測試案例=========================tomcat
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頭無效的問題========================bash
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配置實例==================jsp
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。 因而觀察13六、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; 五、6二者的區別: 在只有一個代理服務器的轉發的狀況下,二者的效果貌似差很少,均可以真實的顯示出客戶端原始ip 可是區別在於: $proxy_add_x_forwarded_for變量包含客戶端請求頭中的"X-Forwarded-For",與$remote_addr兩部分,他們之間用逗號分開。 ################################################################################################################## 舉個例子,有一個web應用,在它以前經過了兩個nginx轉發,www.kevin.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",這樣就清楚了吧。
###### Nginx反向代理Tomcat訪問報錯400問題 #######
線上用nginx反向代理tomcat訪問,配置完成後,直接訪問tomcat徹底正常,可是隻要在nginx添加反向代理tomcat,訪問nginx就會報錯400。
緣由和解決辦法:
1)後端服務器設置有相似防盜鏈或者根據http請求頭中的host字段來進行路由或判斷功能的話,若是nginx代理層不重寫請求頭中的host字段,將會致使請求失敗,報400錯誤。
解決辦法:
proxy_set_header Host $http_host;
2)nginx配置中header頭部信息的host不能被配置重了。tomcat沒有對headers中的host進行惟一校驗。
解決辦法(下面兩個要去掉一個):
proxy_set_header Host $host; proxy_set_header Host $http_host; #去掉這一行