1. 程式人生 > 實用技巧 >十二、rewrite規則

十二、rewrite規則

一、rewrite規則說明

Nginx rewrite的主要功能也是實現URL地址重寫。Nginx的rewrite規則需要PCRE軟體的支援,即通過Perl相容正則表示式語法進行規則匹配。前文在安裝Nginx軟體時就已經安裝了這個PCRE軟體,同時也讓Nginx支援了rewrite的功能,預設引數編譯時,Nginx就會安裝支援rewrite的模組,但是,也必須要有PCRE軟體的支援。

二、rewrite語法引數

1.rewrite指令語法

指令語法:rewrite regex replacement[flag];
預設值:none
應用位置:server、location、if
rewrite是實現URL重寫的關鍵指令,根據regex(正則表示式)部分的內容,重定向到replacement部分,結尾是flag標記。

如果指定的正則表示式與請求URI匹配,則URI將按照*replacement*字串中的指定進行更改。該rewrite指令在其在配置檔案中出現的順序順序地執行。可以使用標誌終止指令的進一步處理。如果替換字串以“ http://”,“ https://”或“ $scheme” 開頭,則處理停止,並將重定向返回給客戶端。

可選flag引數可以是以下之一:
last: 停止處理當前ngx_http_rewrite_module指令集, 並開始搜尋與更改後的URI相匹配的新位置;
break: ngx_http_rewrite_module與break指令一樣, 停止處理當前的指令集 ;
redirect: 返回帶有302程式碼的臨時重定向;如果替換字串不是以“ http:
//”,“ https://”或“ $scheme” 開頭,則使用 permanent: 返回帶有301程式碼的永久重定向。

示例1:

使用者訪問www.ywx.com/image----->www.ywx.com/tupian
也可以當用戶訪問http://www.ywx.com----->https://www.ywx.com

示例2:

一個簡單的URL rewrite跳轉的例子:

rewrite ^/(.*) http://www.ywx.org/$1 permanent;
#在上述指令中,rewrite為固定關鍵字,表示開啟一條rewrite匹配規則,regex部分是^/(.*),這是一個正則表示式,表示匹配所有,匹配成功後跳轉到http://
www.ywx.org/$1。這裡的$1是取前面regex部分括號裡的內容,結尾的permanent;是永久301重定向標記,即跳轉到後面的http://www.ywx.org/$1地址上。

2.regex常用正則表示式說明

rewrite regex replacement [flag]
將使用者請求的URI基於regex所描述的模式進行檢查,匹配到時將其替換為replacement指定的新的URI
注意:如果在同一級配置塊中存在多個rewrite規則,那麼會自下而下逐個檢查;被某條件規則替換完成後,會重新一輪的替換檢查
隱含有迴圈機制,但不超過10次;如果超過,提示500響應碼,[flag]所表示的標誌位用於控制此迴圈機制
如果replacement是以http://或https://開頭,則替換結果會直接以重向返回給客戶端, 即永久重定向301

正則表示式

 \:後面接著的字元標記未一個特殊字元或一個原義字元或一個後向引用。eg."\n"匹配一個換行符,"\\""\$"則匹配“$”

 ^:匹配輸入字串的起始位置

 $:匹配輸入字串的結束位置

 *:匹配前面的字元0次或多次

 +:匹配前面的字元1次或多次

 ?:匹配前面的字元0次或1次

 .:匹配除"\n"之外的任何單個字元,要匹配“\n”,則使用“[.\n]”

3.rewrite指令結尾的flag標記說明

rewrite指令的最後一項引數為flag標記

last 持續匹配新的uri資源

重寫完成後停止對當前URI在當前location中後續的其它重寫操作,而後對新的URI啟動新一輪重寫檢查;提前重啟新一輪迴圈

break 匹配到第一個uri資源後就終止

重寫完成後停止對當前URI在當前location中後續的其它重寫操作,而後直接跳轉至重寫規則配置塊後的其它配置;結束迴圈
last 和 break的比較

break:匹配成功後不再向下匹配,也不會跳轉到其他的location,即直接結束匹配並給客戶端返回結果資料
last:對某個location的URL匹配成功後會停止當前location的後續rewrite規則,並結束當前location,然後將匹配生成的新URL跳轉至其他location繼續匹配,直到沒有location可匹配後將最後一次location的資料返回給客戶端

示例:

location /break {
  rewrite ^/break/(.*) /test/$1 break; #break不會跳轉到其他的location
  return 666 "break";
}
location /last {
  rewrite ^/last/(.*) /test/$1 last; #last會跳轉到其他的location繼續匹配新的URI
  return 888 "last";
}
location /test {
  return 999 "test";
  index index.html;
  root /data/nginx;
}
mkdir /data/nginx/test/
echo test Page > /data/nginx/test/index.html

redirect 臨時跳轉到uri資源資訊

臨時重定向,重寫完成後以臨時重定向方式直接返回重寫後生成的新URI給客戶端,由客戶端重新發起請求;可使用相對路徑,或http://或https://開頭,此重定向資訊不可快取,狀態碼:302

permanent 永久跳轉

重寫完成後以永久重定向方式直接返回重寫後生成的新URI給客戶端,由客戶端重新發起請求,此重定向資訊可快取,狀態碼:301

在以上的flag標記中,last和break用來實現URL重寫,瀏覽器位址列的URL地址不變,但在伺服器端訪問的程式及路徑發生了變化。redirect和permanent用來實現URL跳轉,瀏覽器位址列會顯示跳轉後的URL地址。

last和break標記的實現功能類似,但二者之間有細微的差別,使用alias指令時必須用last標記,使用proxy_pass指令時要使用break標記。last標記在本條rewrite規則執行完畢後,會對其所在的server{………}標籤重新發起請求,而break標記則會在本條規則匹配完成後,終止匹配,不再匹配後面的規則。

示例1:

要求:將 http:// 請求跳轉到 https://
生產案例
location / {
  if ($scheme = http ) {
    rewrite / https://www.ywx.com/ redirect;
   }
}

示例2:

要求:當用戶訪問到公司網站的時輸入了一個錯誤的URL,可以將使用者重定向至官網首頁
生產案例
location / {
  root /data/nginx/html/pc;
  index index.html;
  if (!-f $request_filename) {
  #return 404 "No exsit";
  rewrite (.*) http://www.ywx.com/index.html;
  }
}

4、return code

Syntax:    return code [text];
           return code URL;
           return URL;
Default:    —
Context:    server, location, if

停止處理並將指定的返回*code*給客戶端。非標準程式碼444關閉連線而不傳送響應頭。

return code [text]; #返回客戶端指定的狀態碼和文字說明
return code URL;
return URL;
停止處理,並返回給客戶端指定的響應碼(包括: 204, 400, 402406, 408, 410, 411, 413, 416, 500504),並對 301, 302, 303, 307, 308跳轉到URL

示例:

location /test {
  index index.html;
  default_type text/html;
  if ( $scheme = http ){
    return 301 https://www.ywx.com;
  }
  if ( $scheme = https ){
    echo "if ----> $scheme";
  }
  
  #當用戶訪問www.ywx.com/test時:
  #如果使用https://www.ywx.com/test則返回echo資訊
  #如果使用http://www.ywx.com/test,返回301code並跳轉到https://www.ywx.com/test

5、rewrite_log

Syntax:    rewrite_log on | off;
Default:    rewrite_log off;
Context:    http, server, location, if

是否開啟重寫日誌, 傳送至error_log(notice level)

示例:

location /test {
  root /data/nginx/html/pc;
  default_type text/html;
  index index.html;
  if ( $scheme = http ){
    #return 666;
    #return 666 "not allow http";
    #return 301 http://www.baidu.com;
    return 500 "service error";
    echo "if-----> $scheme"; #return後面的將不再執行
  }
  if ( $scheme = https ){
    echo "if ----> $scheme";
  }
}

6、rewrite中的if語句

Syntax:    if (condition) { ... }
Default:    —
Context:    server, location

條件滿足時,執行配置塊中的配置指令。

condition:常與nginx的內建變數,$scheme,$http_user_agent,$http_cookie,$request_method...配合使用

比較操作符:

1、變數名;如果變數的值為空字串或“ 0”,則為false;否則為false 。(在1.0.1版之前,任何以“ 0” 開頭的字串都被視為錯誤值。)
2、使用“ =”和“ !=”運算子將變數與字串進行比較;
3、使用“ ~”(用於區分大小寫的匹配)和“ ~*”(用於不區分大小寫的匹配)運算子將變數與正則表示式進行匹配。正則表示式可以包含捕獲,這些捕獲可用於以後在$1.. $9變數中重用。
4、使用“ !~(區分大小寫)”模式不匹配和“ !~*(不區分大小寫)”的模式不匹配。如果正則表示式包含“ }”或“ ;”字元,則整個表示式應用單引號或雙引號引起來。
5、使用“ -f”和“ !-f”運算子檢查檔案是否存在;
6、使用“ -d”和“ !-d”運算子檢查目錄是否存在;
7、使用“ -e”和“ !-e”運算子檢查檔案,目錄或符號連結是否存在;
8、使用“ -x”和“ !-x”運算子檢查可執行檔案。

示例1:

location /test {
  index index.html;
  default_type text/html;
  if ( $scheme = http ){
    return 301 https://www.ywx.com;
  }
  if ( $scheme = https ){
    echo "if ----> $scheme";
  }
  
  #當用戶訪問www.ywx.com/test時:
  #如果使用https://www.ywx.com/test則返回echo資訊
  #如果使用http://www.ywx.com/test,返回301code並跳轉到https://www.ywx.com/test

示例2:

if (-f $request_filename) {
   echo "file is exist";
}
if (!-f $request_filename) {
   echo "file is not exist";
   return 409;
}
#$request_filename檔案存在則返回"file is exist";
#$request_filename檔案不存在則返回"file is not exist",code程式碼=409

三、案例

1、Nginx rewrite 301跳轉

以往我們是通過別名的方式實現etiantian.org和www.etiantian.org訪問同一個地址的,事實上,除了這個方式以外,還可以使用Nginx rewrite 301跳轉的方式來實現。實現的配置如下:

[root@www extra]# cat www.conf
server {
  listen   80;
  server_name  etiantian.org;
  rewrite ^/(.*) http://www.etiantian.org/$1 permanent;
#<==當用戶訪問etiantian.org及下面的任意內容時,都會通過這條rewrite跳轉www.etiantian.org對應的地址
#如訪問etiantian.org/index.html跳轉到www.etiantian.org/index.html
 }

#為了避免返回404,應該有ww.etiantian.org的地址
server {
  listen   80;
  server_name  www.etiantian.org;
  location / {
    root   html/www;
    index  index.html index.htm;
  }
  access_log logs/access_www.log main gzip buffer=32k flush=5s;
}

2、實現不同域名的URL跳轉

例1:實現訪問http://blog.etiantian.org時跳轉到http://www.etiantian.org/blog/oldboy.html

外部跳轉時使用這種方法,可讓瀏覽器地址變為跳轉後的地址,另外,要事先設定http://www.etiantian.org/blog/oldboy.html有結果輸出,不然會出現401等許可權錯誤。

1.配置Nginx rewrite規則

跳轉前,http://blog.etiantian.org對應站點的配置如下:

[root@www extra]# cat blog.conf
server {
  listen   80;
  server_name  blog.etiantian.org;
  location / {
    root   html/blog;
    index  index.html index.htm;
  }
  if ( $http_host ~* "^(.*)\.etiantian\.org$") { 
    set $domain $1; 
    rewrite ^(.*) http://www.etiantian.org/$domain/oldboy.html break; 
  } 
}

要配置的規則內容為:

if ( $http_host ~* "^(.*)\.etiantian\.org$") { 
 set $domain $1; 
 rewrite ^(.*) http://www.etiantian.org/$domain/oldboy.html break; 
}

跳轉後,http://www.etiantian.org/blog/oldboy.html地址對應的站點配置如下:

[root@www extra]# cat www.conf
server {
  listen   80;
  server_name  www.etiantian.org etiantian.org;
  location / {
    root   html/www;
    index  index.html index.htm;
  }
  access_log logs/access_www.log main gzip buffer=32k flush=5s;
}

例2:實現訪問http://etiantian.org/bbs時跳轉到http://bbs.etiantian.org

在etiantian.org下設定Nginx rewrite規則,如下:

[root@www extra]# cat www.conf
server {
  listen   80;
  server_name  www.etiantian.org etiantian.org;
  location / {
    root   html/www;
    index  index.html index.htm;
  }
  rewrite ^(.*)/bbs/  http://bbs.etiantian.org break;
  access_log logs/access_www.log main gzip buffer=32k flush=5s;
}

配置bbs.etiantian.org的站點內容

[root@www extra]# cat bbs.conf
server {
  listen   80;
  server_name  bbs.etiantian.org;
  location / {
    root   html/bbs;
    index  index.html index.htm;
  }
}

在根location(即location/{……})中或server{……}標籤中編寫rewrite規則,建議使用last標記,而在普通的location(例location/oldboy/{……}或if{})中編寫rewrite規則,則建議使用break標記。

例3:實現域名地址資訊跳轉

用於做偽靜態

www.etiantian.org/oldboy?edu.html ---動態資源

www.etiantian.org/oldboy-edu.html ---偽靜態

實現類似百度重寫域名的功能?

baidu.com ===> www.baidu.com

etiantian.org ===> www.etiantian.org

rewrite指令實踐操作一:(錯誤)

[root@web01 extra]# cat bbs.conf 
server {
  listen 80;
  server_name www.etiantian.org bbs.org;
  rewrite ^/(.*) http://www.etiantian.org/$1 permanent; ----此處用到了perl語言的正則表示式
  root html/bbs;
  index index.html index.htm;
}

[root@web01 extra]# curl -L etiantian.org 
curl: (47) Maximum (50) redirects followed
[root@web01 extra]# curl -Lv etiantian.org --- 顯示無限迴圈過程

說明:以上配置進入了無限迴圈狀態

rewrite指令實踐操作二:(正確)

cat bbs.conf 
server {
  listen 80;
  server_name etiantian.org;
  rewrite ^/(.*) http://bbs.etiantian.org/$1 permanent;
}
server {
  listen 80;
  server_name bbs.etiantian.org bbs.org;
  root html/bbs;
  index index.html index.htm;
}

rewrite指令實踐操作三:(正確)

[root@web01 extra]# cat bbs.conf 
server {
  listen 80;
  server_name bbs.etiantian.org bbs.org;
  if ($host ~ "^etiantian.org$") {
  rewrite ^/(.*) http://bbs.etiantian.org/$1 permanent;
  }
  root html/bbs;
  index index.html index.htm;
}

訪問bbs.ywx.com跳轉到www.ywx.com/bbs/ywx.html 首先要www.ywx.com/bbs/ywx.html的內容可以被訪問

server {
   listen 80;
   server_name   www.ywx.com;
   root   html/www;
   index  index.html index.htm;
   if ( $http_host ~* "^bbs\.ywx\.com$" ) {

   rewrite ^(.*) http://www.ywx.com/bbs/ywx.html break;

   }
}

或者

server{
   listen 80;
   server_name  bbs.ywx.com;
   rewrite  ^(.*) http://www.ywx.com/bbs/ywx.html
}  
server {
   listen  80;
   server_name   www.ywx.com;
   root   html/www;
   index  index.html index.htm;      
}

3、客戶訪問http請求,則跳轉https://10.0.0.101;客戶訪問的頁面不存在,則跳轉到https://10.0.0.101

nginx安裝部署略過

nginx的server的配置

 server {
        server_name  localhost;
        listen 443 ssl;
        ssl_certificate /etc/pki/CA/certs/nginx.crt;
        ssl_certificate_key /data/nginx.key;
        ssl_session_cache shared:sslcache:20m;
        ssl_session_timeout 10m;
        location / {
            root   html;
            index  index.html index.htm;
            default_type test/html;
        }
    
 }

  server {
        server_name  localhost;
        listen 80;
        location / {
            root   html;
            index  index.html index.htm;
            default_type test/html;
            if ( $scheme = http ){
                return 301 https://10.0.0.101;
            }
            if ( $scheme = https ){
                echo "if ----> $scheme";
            }
           if ( !-f $request_filename ) {
                rewrite (.*) https://10.0.0.101;
            }
        }

   }

重新載入配置檔案

nginx -t
nginx -s reload

測試訪問

在非nginx伺服器上,我是在在10.0.0.102上訪問

[root@node2 ~]# curl http://10.0.0.101:80
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.16.0</center>
</body>
</html>
#301跳轉

[root@node2 ~]# curl -L http://10.0.0.101:80
curl: (60) Peer's Certificate issuer is not recognized.
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.
#跳轉到https

[root@node2 ~]# curl -Lk http://10.0.0.101:80
ni hao nginx!!!
訪問http自動跳轉為https


訪問一個不存在的頁面
[root@node2 ~]# curl http://10.0.0.101:80/kkkkkkkkkkkkk
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.16.0</center>
</body>
</html>
[root@node2 ~]# curl -L http://10.0.0.101:80/kkkkkkkkkkkkk
curl: (60) Peer's Certificate issuer is not recognized.
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.
[root@node2 ~]# curl -Lk http://10.0.0.101:80/kkkkkkkkkkkkk
ni hao nginx!!!
#最後會跳轉到https://10.0.0.101