Nginx是一個免費開源且穩定高效的Web伺服器程式,擁有反向代理以及負載平衡的功能,經常作為最前端的伺服器,也很常用來實現「轉址」(URL redirection)功能。



Nginx的ngx_http_rewrite_module模組

「ngx_http_rewrite_module」是Nginx預設啟用的模組,可以用來將符合某個正規表示式的網址轉成別的網址。另外,它也提供了一些如ifbreakreturn等常用的流程控制命令。

若要使用ngx_http_rewrite_module模組來實現轉址功能,我們可以在server區塊或是其子區塊內使用「ngx_http_rewrite_module」模組提供的rewrite命令。例如:

rewrite ^ /123;

以上命令,可以將所有的網址都轉址到/123,並且繼續執行同一個區塊後續的命令。也就是說,/123也還是可以之後再使用「ngx_http_rewrite_module」模組所提供的命令轉成其它位址。這邊要注意的是,rewrite命令無法去更動到原始網址中的查詢(Query)參數,也就是問號?和其後面的字串。

rewrite命令也可以加入第三個參數,來進行更細微的控制,第三個參數值可以是以下幾種之一:

  • last使這個rewrite命令的正規表示式成立且轉址(內部)之後,不會再去執行同一個區塊後續的命令,而是會直接以新的位址來去尋找符合的location區塊再重新去處理它。
  • break使這個rewrite命令的正規表示式成立且轉址(內部)之後,不會再去執行同一個區塊後續的命令。
  • redirect使這個rewrite命令的正規表示式成立且轉址之後,直接以HTTP的302狀態(Moved Temporarily)來回應客戶端,使其暫時地使用新網址再發送一次請求。
  • permanent使這個rewrite命令的正規表示式成立且轉址之後,直接以HTTP的301狀態(Move Permanently)來回應客戶端,使其使用新網址再發送一次請求,並讓客戶端知道原本的網址已經被永久轉移到新網址了。

如果rewrite命令的第三個參數不是redirect或是permanent,則轉移過去的位址最好只能是以斜線/開頭的路徑,也就是要在同一個server區塊下處理的資源。反之,如果要在不同的server區塊甚至是不同的伺服器主機間進行轉址的話,rewrite命令的第三個參數就使用redirect或是permanent吧!

藉由正規表示式的群組功能,我們可以將原先位址中的部份字串,動態填入至新的位址中。例如:

rewrite ^/api/(.*)$ /api/v1/$1;

以上命令,可以將/api/xxx或是/apt/xxx/yyy等位址,轉成/api/v1/xxx或是/apt/v1/xxx/yyy

另外,在新的位址中,也可以使用$scheme變數來取得原先網址中所使用的通訊協定。例如:

rewrite ^/(.*)$ $scheme://new-domain.com/$1 redirect;

以上命令,可以將所有的網址轉到新的網域(new-domain.com),並且保留原先網址的通訊協定。當然,以這個例子來說,比較好的作法是,直接利用$request_uri變數來取得原先網址中不包括前段通訊協定和伺服器名稱的內容,如此一來就可以省下使用正規表示式來處理的運算資源。如下:

rewrite ^ $scheme://new-domain.com$request_uri? redirect;

別忘了在使用$request_uri變數時,最好在其後加上問號?,才能避免網址的查詢參數被重複添加。例如原本的網址是http://127.0.0.1/api?a=10,如果沒有在$request_uri變數之後加上問號?,轉出來就會是http://new-domain.com/api?a=10?a=10

如果想進行更進階的控制,可以使用ifbreakreturn命令。if命令可以判斷一個變數是否不是0false或是空字串,如果不是的話,就會去執行該if區塊中的命令。break可以略過同一個區塊中後續要執行的命令。return命令會直接發送HTTP回應,完成路由。return命令的參數必須要有HTTP狀態碼或是要轉址到的新網址,如果兩者都有的話,HTTP狀態碼為第一個參數,網址則為第二個參數。

例如以下命令,直接以HTTP的403狀態(Forbidden)來回應客戶端。

return 403;

例如以下命令,直接以HTTP的302狀態(Moved Temporarily)來回應客戶端,使其暫時地使用新網址再發送一次請求。

return 302 https://magiclen.org;

常用的轉址設定

底下以不同的情境,來區分Nginx的設定檔寫法。

整個網域都轉到一個固定的網址(302轉址)

server {
    ...
 
    server_name 網域名稱;
 
    rewrite ^ 固定網址 redirect;
}

同網域下,HTTP轉HTTPS(永遠使用https)

server {
    listen 80; 
    listen [::]:80;
 
    server_name 網域名稱;
 
    rewrite ^ https://網域名稱$request_uri? permanent;
}

舊網域換新網域(301轉址)

server {
    ...

    server_name 舊網域名稱;

    rewrite ^ $scheme://新網域名稱$request_uri? permanent;
}

同網域轉移目錄(301轉址)

server {
    ...

    server_name 網域名稱;

    location ^~ /舊目錄名稱/ {
        rewrite ^/舊目錄名稱/(.*)$ /新目錄名稱/$1 permanent;
    }
}

此處location命令所使用的^~,表示要判斷路徑前綴是否吻合。

將Web應用程式掛入至現有網域的某個目錄下

server {
    ...

    server_name 網域名稱;
 
    location ^~ /目錄名稱/ {
        proxy_pass Web應用程式的通訊協定(通常是http)://Web應用程式綁定的網路介面IP:Web應用程式綁定的連接埠[/要掛入的Web應用程式網址路徑];
    }
}