1. 程式人生 > 其它 >Nginx常用反向代理配置規則

Nginx常用反向代理配置規則

Nginx常用反向代理配置規則

https://segmentfault.com/a/1190000022501310

因為業務系統需求,需要對web服務作nginx代理,在不斷的嘗試過程中,簡單總結了一下常見的nginx代理配置。

1. 最簡反向代理配置

在http節點下,使用upstream配置服務地址,使用server的location配置代理對映。

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/。

需要注意的是,如果按照如下配置:

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識別的路徑作為絕對路徑。

2. 重定向報文代理

即便配置了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/域名和埠),有時候會填寫相對路徑,此時需要根據實際情況進行甄別。

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字首可以省略。

3. 報文資料替換

使用nginx代理最牛(dan)逼(sui)的情況就是http響應報文內寫死了服務地址或web絕對路徑。寫死服務地址的情況比較少見,但也偶爾存在。最棘手的是寫死了web絕對路徑,尤其是絕對路徑都沒有公共字首。舉個例子來說:

一般的web頁面會包含如下類似路徑:

  • /public:用於靜態頁面資源,如js指令碼/public/js,樣式表/public/css,圖片/public/img等。
  • /static:和/public類似。
  • /api:用於後臺服務API介面。
  • /login:用於登入驗證。
  • 其他。

對於這樣的服務,可能的代理配置如下:

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版本之上的。

4. 總結

即便如此,sub_filter也不能解決所有問題。目前流行的js框架都會有自動渲染url的功能,也就是說,很多絕對路徑並非寫死在靜態頁面內,也是由js程式碼框架動態生成的,面對這樣的情況,sub_filter也是無能為力了。對於這樣的情況,筆者只能由衷地奉勸,還是安靜的改程式碼吧!

============ End