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



Nginx的ngx_http_rewrite_module模組

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

若要使用「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」。

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

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

return 403;

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

return 302 https://magiclen.org;

常用的轉址設定

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

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

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

同網域下,HTTP轉HTTPS

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

舊網域換新網域

server {
    ...

    server_name 舊網域名稱;

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

同網域轉移目錄

server {
    ...

    server_name 網域名稱;

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

此處「location」命令所使用的「^~」,表示要從路徑開頭來使用正規表示式進行比對,且必須判斷大小寫(case-sensitive)。