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的設定檔寫法。
整個網域都轉到一個固定的網址(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應用程式網址路徑];
}
}