由於業務系統需求,須要對web服務做nginx代理,在不斷的嘗試過程當中,簡單總結了一下常見的nginx代理配置。css
在http節點下,使用upstream配置服務地址,使用server的location配置代理映射。html
upstream my_server { server 10.0.0.2:8080; keepalive 2000; } server { listen 80; server_name 10.0.0.1; client_max_body_size 1024M; location /my/ { proxy_pass http://my_server/; proxy_set_header Host $host:$server_port; } }
經過該配置,訪問nginx地址http://10.0.0.1:80/my的請求會被轉發到my_server服務地址http://10.0.0.2:8080/。nginx
須要注意的是,若是按照以下配置:web
upstream my_server { server 10.0.0.2:8080; keepalive 2000; } server { listen 80; server_name 10.0.0.1; client_max_body_size 1024M; location /my/ { proxy_pass http://my_server; proxy_set_header Host $host:$server_port; } }
那麼,訪問nginx地址http://10.0.0.1:80/my的請求會被轉發到my_server服務地址http://10.0.0.2:8080/my。這是由於proxy_pass參數中若是不包含url的路徑,則會將location的pattern識別的路徑做爲絕對路徑。api
即使配置了nginx代理,當服務返回重定向報文時(http code爲301或302),會將重定向的目標url地址放入http response報文的header的location字段內。用戶瀏覽器收到重定向報文時,會解析出該字段並做跳轉。此時新的請求報文將直接發送給服務地址,而非nginx地址。爲了能讓nginx攔截此類請求,必須修改重定向報文的location信息。瀏覽器
location /my/ { proxy_pass http://my_server; proxy_set_header Host $host:$server_port; proxy_redirect / /my/; }
使用proxy_redirect能夠修改重定向報文的location字段,例子中會將全部的根路徑下的url代理到nginx的/my/路徑下返回給用戶。好比服務返回的重定向報文的location原始值爲/login,那麼通過nginx代理後,用戶收到的報文的location字段爲/my/login。此時,瀏覽器將會跳轉到nginx的/my/login地址進行訪問。框架
須要注意的是,服務返回的重定向報文的location字段有時會填寫絕對路徑(包含服務的ip/域名和端口),有時候會填寫相對路徑,此時須要根據實際狀況進行甄別。ui
location /my/ { proxy_pass http://my_server; proxy_set_header Host $host:$server_port; proxy_redirect http://my_server/ http://$host:$server_port/my/; }
上述配置即是將my_server服務的根路徑下的全部路徑代理到nginx地址的/my/路徑下。當nginx配置只有一個server時,http://$host:$server_port
前綴能夠省略。url
使用nginx代理最牛(dan)逼(sui)的狀況就是http響應報文內寫死了服務地址或web絕對路徑。寫死服務地址的狀況比較少見,但也偶爾存在。最棘手的是寫死了web絕對路徑,尤爲是絕對路徑都沒有公共前綴。舉個例子來講:代理
通常的web頁面會包含以下相似路徑:
對於這樣的服務,可能的代理配置以下:
location /my/ { proxy_pass http://my_server/; proxy_set_header Host $host:$server_port; proxy_redirect / /my/; } location /login/ { proxy_pass http://my_server/public; proxy_set_header Host $host:$server_port; } location /public/ { proxy_pass http://my_server/public; proxy_set_header Host $host:$server_port; } location /api/ { proxy_pass http://my_server/api; proxy_set_header Host $host:$server_port; }
因爲web頁面或靜態資源內寫死了相似的絕對路徑,那麼對於用戶來講,經過頁面內的連接進行跳轉時,都會請求到nginx服務對應的路徑上。一旦存在另外一個服務也包含相似的路徑,也須要nginx進行代理,那麼矛盾就出現了:訪問nginx的同一個路徑下的請求究竟轉發給哪個服務?
要解決這個問題,必須在用戶收到報文前,將報文的數據中包含的絕對路徑都添加統一的前綴,如/my/public,/my/api,/my/login,這樣nginx代理配置則能夠簡化爲:
location /my/ { proxy_pass http://my_server/; proxy_set_header Host $host:$server_port; proxy_redirect / /my/; } location /other/ { proxy_pass http://other_server/; proxy_set_header Host $host:$server_port; proxy_redirect / /other/; }
nginx的ngx_http_sub_module模塊提供了相似的報文數據替換功能,該模塊默認不會安裝,須要在編譯nginx時添加--with-http_sub_module參數,或者直接下載nginx的rpm包。
使用sub_filter對數據包進行替換的語法以下:
location /my/ { proxy_pass http://my_server/; proxy_set_header Host $host:$server_port; sub_filter 'href="/' 'href="/my/'; sub_filter 'src="/' 'src="/my/'; sub_filter_types text/html; sub_filter_once off; }
上述配置會將/my/下的全部響應報文內容的href="/替換爲href="/my,以及src="/替換爲src="/my,即爲全部的絕對路徑添加公共前綴。
注意,若是須要配置多個sub_filter,必須保證nginx是1.9.4版本之上的。
即使如此,sub_filter也不能解決全部問題。目前流行的js框架都會有自動渲染url的功能,也就是說,不少絕對路徑並不是寫死在靜態頁面內,也是由js代碼框架動態生成的,面對這樣的狀況,sub_filter也是無能爲力了。對於這樣的狀況,筆者只能由衷地奉勸,仍是安靜的改代碼吧!