nginx之旅(第五篇):URL重寫介紹、URL重寫場景、URL重寫語法


nginx之旅(第五篇):URL重寫

 

一、URL重寫介紹

URL重寫是指將一個URL請求重新寫成網站可以處理的另一個URL的過程。這樣說可能不是很好理解,舉個例子來說明一下,在開發中可能經常遇到這樣的需求,比如通過瀏覽器請求的http://localhost:8080/getUser?id=1,但是需要通過SEO優化等等原因,需要把請求的地址重寫為http://localhost:8080/getUser/1這樣的URL,從而符合需求或者更好的被網站閱讀。

當遇到這種請求的時候,就需要使用到UrlRewrite重寫或者使用一些網關路由,如SpringCloud的Gateway,Zuul,又或者是Nginx來實現這個功能。

和apache等web服務軟件一樣,rewrite的主要功能是實現URL地址的重定向。Nginx的rewrite功能需要PCRE軟件的支持,即通過perl兼容正則表達式語句進行規則匹配的。默認參數編譯nginx就會支持rewrite的模塊,但是也必須要PCRE的支持。

rewrite和location的功能有點相像,都能實現跳轉,主要區別在於rewrite常用於同一域名內更改獲取資源的路徑,而location是對一類路徑做控制訪問和反向代理,可以proxy_pass到其他服務器。

Nginx提供的全局變量或自己設置的變量,結合正則表達式和標志位實現url重寫以及重定向。 rewrite只能放在server{},location{},if{}中, 並且只能對域名后邊的除去傳遞的參數外的字符串起作用。

Rewrite主要的功能就是實現URL的重寫,Nginx的Rewrite規則采用Pcre,perl兼容正則表達式的語法規則匹配,如果需要Nginx的Rewrite功能,在編譯Nginx之前,需要編譯安裝PCRE庫。 通過Rewrite規則,可以實現規范的URL、根據變量來做URL轉向及選擇配置。

 

二、URL重寫應用場景

域名變更 (京東)

用戶跳轉 (從某個連接跳到另一個連接)

偽靜態場景 (便於CDN緩存動態頁面數據)

 

 

三、URL重寫語法

URL重寫語法

rewrite     <regex>     <replacement>     [flag];
​
關鍵字        正則            替代內容          flag標記
​
​
regex :可以使用正則或者字符串來表示相匹配的地址。
replacement:可以表示重定向的地址。
flag :flag標志的作用是用於控制當匹配到對應的rewrite規則后是否繼續檢查后續的rewrite規則。
flag值為如下四種,分別是:
​
last:停止處理當前的rewrite指令集,而后通過重寫后的規則重新發起請求,瀏覽器地址欄URL地址不變。
break:和break指令一樣,都是停止處理當前上下文中的其他重寫模塊指令。
redirect:如果替換字符串不以“ http://”,“ https://”或“ $scheme” 開頭,返回帶有302代碼的臨時重定向,瀏覽器地址會顯示跳轉后的URL地址。
permanent:返回301代碼的永久重定向,瀏覽器地址欄會顯示跳轉后的URL地址。

  


rewrite參數的標簽可使用的位置

應用位置:server、location、if

nginx rewrite指令執行順序

1.執行server塊的rewrite指令(這里的塊指的是server關鍵字后{}包圍的區域,其它xx塊類似) 2.執行location匹配 3.執行選定的location中的rewrite指令

如果其中某步URI被重寫,則重新循環執行1-3,直到找到真實存在的文件。

如果循環超過10次,則返回500 Internal Server Error錯誤。

 

1) set設置變量量

所有的 Nginx變量在 Nginx 配置文件中引用時都須帶上 $ 前綴

在 Nginx 配置中,變量只能存放一種類型的值,有且也只存在一種類型,那就是字符串類型

set指令 自定義變量量
Syntax:
set $variable value;
set關鍵字  $變量名 變量值
Default:
—
Context:
server, location,

  

例:

訪問主機ip本來應該進入http://www.cnblogs.com  重寫為 http://www.cnblogs.com/Nicholas0707
location / {
                set $name Nicholas0707;
                rewrite ^(.*)$ http://www.cnblogs.com/$name;
        }

  

 

 

變量創建,賦值及作用域問題

 

  • 變量的創建和賦值操作發生在全然不同的時間階段。Nginx 變量的創建只能發生在 Nginx 配置加載的時候,或者說 Nginx 啟動的時候;而賦值操作則只會發生在請求實際處理的時候。這意味着不創建而直接使用變量會導致啟動失敗,同時也意味着我們無法在請求處理時動態地創建新的 Nginx 變量。

  • Nginx 變量一旦創建,其變量名的可見范圍就是整個 Nginx 配置,甚至可以跨越不同虛擬主機的 server 配置塊

  • Nginx變量名的可見范圍雖然是整個配置,但每個請求都有所有變量的獨立副本,或者說都有各變量用來存放值的容器的獨立副本,彼此互不干擾

例:

server {
       listen 8080;
​
       location /foo {
           echo "foo = [$foo]";
       }
​
       location /bar {
           set $foo 32;
           echo "foo = [$foo]";
       }
   }

  

結果:

$ curl 'http://localhost:8080/foo'
    foo = []
 
$ curl 'http://localhost:8080/bar'
foo = [32]
 
$ curl 'http://localhost:8080/foo'
foo = []

  

分析:從這個例子我們可以看到,set 指令因為是在 location /bar 中使用的,所以賦值操作只會在訪問 /bar 的請求中執行。而請求 /foo 接口時,我們總是得到空的 $foo 值,因為用戶變量未賦值就輸出的話,得到的便是空字符串。

 

在不同層級的標簽中聲明的變量性的可見性規則如下:

  1. location標簽中聲明的變量中對這個location塊可見

  2. server標簽中聲明的變量對server塊以及server塊中的所有子塊可見

  3. http標簽中聲明的變量對http塊以及http塊中的所有子塊可見

NGINX內置預定義變量


內置預定義變量即無需聲明就可以使用的變量,通常包括一個http請求或響應中一部分內容的值,以下為一些常用的內置預定義變量

變量名 定義
$arg_PARAMETER  GET請求中變量名PARAMETER參數的值。
$args   這個變量等於GET請求中的參數。例如,foo=123&bar=blahblah;這個變量只可以被修改
$binary_remote_addr 二進制碼形式的客戶端地址。
$body_bytes_sent    傳送頁面的字節數
$content_length 請求頭中的Content-length字段。
$content_type   請求頭中的Content-Type字段。
$cookie_COOKIE  cookie COOKIE的值。
$document_root  當前請求在root指令中指定的值。
$document_uri   與$uri相同。
$host   請求中的主機頭(Host)字段,如果請求中的主機頭不可用或者空,則為處理請求的server名稱(處理請求的server的server_name指令的值)。值為小寫,不包含端口。
$hostname   機器名使用 gethostname系統調用的值
$http_HEADER    HTTP請求頭中的內容,HEADER為HTTP請求中的內容轉為小寫,-變為_(破折號變為下划線),例如:$http_user_agent(Uaer-Agent的值);
$http_user_agent : 客戶端agent信息;
$http_cookie : 客戶端cookie信息;
$sent_http_HEADER   HTTP響應頭中的內容,HEADER為HTTP響應中的內容轉為小寫,-變為_(破折號變為下划線),例如: $sent_http_cache_control, $sent_http_content_type…;
$is_args    如果$args設置,值為"?",否則為""。
$limit_rate 這個變量可以限制連接速率。
$nginx_version  當前運行的nginx版本號。
$query_string   與$args相同。
$remote_addr    客戶端的IP地址。
$remote_port    客戶端的端口。
$remote_user    已經經過Auth Basic Module驗證的用戶名。
$request_filename   當前連接請求的文件路徑,由root或alias指令與URI請求生成。
$request_body   這個變量(0.7.58+)包含請求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比較有意義。
$request_body_file  客戶端請求主體信息的臨時文件名。
$request_completion 如果請求成功,設為"OK";如果請求未完成或者不是一系列請求中最后一部分則設為空。
$request_method 這個變量是客戶端請求的動作,通常為GET或POST。包括0.8.20及之前的版本中,這個變量總為main request中的動作,如果當前請求是一個子請求,並不使用這個當前請求的動作。
$request_uri    這個變量等於包含一些客戶端請求參數的原始URI,它無法修改,請查看$uri更改或重寫URI,
包含請求參數的原始URI,不包含主機名,如:”/foo/bar.php?arg=baz”。
​
$scheme 所用的協議,比如http或者是https,比如rewrite ^(.+)$ $scheme://example.com$1 redirect;
$server_addr    服務器地址,在完成一次系統調用后可以確定這個值,如果要繞開系統調用,則必須在listen中指定地址並且使用bind參數。
$server_name    服務器名稱。
$server_port    請求到達服務器的端口號。
$server_protocol    請求使用的協議,通常是HTTP/1.0或HTTP/1.1。
$uri    請求中的當前URI(不帶請求參數,參數位於args),不同於瀏覽器傳遞的args),不同於瀏覽器傳遞的args),不同於瀏覽器傳遞的request_uri的值,它可以通過內部重定向,或者使用index指令進行修改。uri不包含主機名,如”/foo/bar.html”。
​

  

 

2) if 負責語句句中的判斷

語法:

Syntax:
​
if (condition) { ... }
​
if (表達式) {
   ... ...
}
​
Default:
​
—
​
Context:
​
server, location
​

 

if語句中conditon規則

當表達式只是一個變量時,如果值為空或任何以0開頭的字符串都會當做false
直接比較變量和內容時,精確匹配 =或!=
​
匹配符號意義:
~區分大小寫正則匹配;
~*不區分大小寫正則匹配; 
!~區分大小寫正則不匹配; 
!~*不區分大小寫正則不匹配;
​
使用“ -f”和“ !-f”運算符檢查文件是否存在;
使用“ -d”和“ !-d”運算符檢查目錄是否存在;
使用“ -e”和“ !-e”運算符檢查文件,目錄或符號鏈接是否存在;
使用“ -x”和“ !-x”運算符檢查可執行文件。

  

server {
    # 如果文件不存在則返回400
    if (!-f $request_filename) {
        return 400;
    }
    
    #如果瀏覽器是chrome瀏覽器則返回403
    if ($http_user_agent ~* 'Chrome') {
    return 403;
    #return http://www.cnblogs.com;
    }
    
​
    
    # 如果請求類型不是POST則返回405
    if ($request_method = POST) {
        return 405;
    }
    
    # 如果參數中有 a=1 則301到指定域名
    if ($args ~ a=1) {
        rewrite ^ http://example.com/ permanent;
    }
}
 

  

3) return 返回返回值或URL

語法

return 指令 定義返回數據
Syntax:
return code [text];
return code URL;
return URL;
Default:
—
Context:
server, location, if

  


#如果瀏覽器是chrome瀏覽器則返回403  其他瀏覽器返回博客園網址
location / {
                root html;
                index index.html index.htm;
                if ($http_user_agent ~* 'Chrome') {
                        return 403;
​
                }
                rewrite ^/$ http://www.cnblogs.com permanent ;
                
        }
 

  

 

4) break 終止后續的rewrite規則

用於停止執行rewrite模塊的指令,但是其他模塊不受影響

語法

Syntax: break;
​
Default:—
​
Context:server, location, if

  


例子

  #如果瀏覽器是chrome瀏覽器則返回403,但這里加了break,就不會執行return 403,返回默認的index,
  #也不會返回博客園
       
   location / {
                if ($http_user_agent ~* 'Chrome') {
                        break;
                        return 403;
                }
                rewrite ^/$ http://www.cnblogs.com permanent ;
                
        }
 

  

5) rewrite 重定向URL

rewrite <regex> <replacement> [flag];
關鍵字 正則 替代內容 flag標記
​
flag:
last
#本條規則匹配完成后,繼續向下匹配新的location URI規則
break
#本條規則匹配完成即終⽌止,不不再匹配后⾯面的任何規則
redirect
#返回302臨時重定向,瀏覽器器地址會顯示跳轉后的URL地址
permanent #返回301永久重定向,瀏覽器器地址欄會顯示跳轉后的URL地址
​
​
重定向就是將網頁自動轉向重定向
301永久性重定向:新網址完全繼承舊網址,舊網址的SEO網絡搜索引擎的排名等完全清零
301重定向是網頁更改地址后對搜索引擎友好的最好方法,只要不是暫時搬移的情況,都建議使用301來做轉址。
302臨時性重定向:對舊網址沒有影響,但新網址不會有排名
搜索引擎爬蟲會抓取新的內容而保留舊的網址

  

 

正則表達式規則

正則表達式匹配,其中:
~       為區分大小寫匹配
~*      為不區分大小寫匹配
!~和!~*  分別為區分大小寫不匹配及不區分大小寫不匹配
​
.      匹配除換行符以外的任意字符
\w     匹配字母或數字或下划線或漢字
\s     匹配任意的空白符
\d     匹配數字
\b     匹配單詞的開始或結束
^      匹配字符串的開始
$      匹配字符串的結束
​
*         重復零次或更多次
+         重復一次或更多次
?         重復零次或一次
{n}       重復n次
{n,}      重復n次或更多次
{n,m}     重復n到m次
*?        復任意次,但盡可能少重復
+?        重復1次或更多次,但盡可能少重復
??        重復0次或1次,但盡可能少重復
{n,m}?    重復n到m次,但盡可能少重復
{n,}?     重復n次以上,但盡可能少重復
​
\W        匹配任意不是字母,數字,下划線,漢字的字符
\S        匹配任意不是空白符的字符
\D        匹配任意非數字的字符
\B        匹配不是單詞開頭或結束的位置
[^x]      匹配除了x以外的任意字符
[^aeiou]  匹配除了aeiou這幾個字母以外的任意字符    
​
(exp)         匹配exp,並捕獲文本到自動命名的組里
(?<name>exp)  匹配exp,並捕獲文本到名稱為name的組里,也可以寫成(?'name'exp)
(?:exp)       匹配exp,不捕獲匹配的文本,也不給此分組分配組號    
(?=exp)       匹配exp前面的位置
(?<=exp)      匹配exp后面的位置
(?!exp)       匹配后面跟的不是exp的位置
(?<!exp)      匹配前面不是exp的位置
(?#comment)   注釋分組不對正則表達式的處理產生任何影響

  

常用的rewrite重寫規則,用來美化網頁的鏈接。規則里面的$1$2你不知道是怎么來的話,只要記住,第一個()里面的是$1,第二個()里面的是$2.

 

server{
​
    # 如果host不是sogou.com,則301到cnblogs.com中
    if ( $host != "sogou.com" ){
        rewrite ^/(.*)$ https://cnblogs.com/$1 permanent;
    }
  
}

  




permanent標志:永久重定向

域名跳轉

域名跳轉,測試前修改host文件 192.168.199.228 www.abc.com
​
www.abc.com     重寫為  www.cnblog.com
server {
        listen        80;
        server_name www.abc.com;
        location / {
            rewrite ^/$ http://www.cnblog.com permanent;
​
          }
}

  

redirect標志:臨時重定向

域名跳轉,測試前修改host文件 192.168.199.228 www.abc.com
​
www.abc.com     重寫為  www.cnblog.com
server {
        listen        80;
        server_name www.abc.com;
        location / {
            rewrite ^/$ http://www.cnblog.com permanent;
​
          }
}

  

 

last標志:

url重寫后,馬上發起一個新的請求,再次進入server塊,重試location匹配,超過10次匹配不到報500錯誤,地址欄url不變

last 一般出現在server或if中

location / {
           rewrite ^/test1 /test2;
           rewrite ^/test2 /test3 last;
           rewrite ^/test3 /test4;
        }
location /test2 {
        return 401;
        }
location /test3 {
        return 402;
        }
location /test4 {
        return 403;
        }

  

 

分析:

測試鏈接:http://192.168.199.328/test1 匹配到 location / {}后,被重寫為/test2,順序執行再次被重寫為/test3,因為flag為last,所以不會繼續重寫為/test4,而是發起一次location匹配,匹配到location /test3{},所以最終返回結果為402;

如果把location /{}中的last改為break,被重寫為/test3后,不再重寫為/test4,也不會發起location,最終沒有可匹配的資源,返回http404。

 

 

 

參考資料

[1]https://www.jianshu.com/p/f62b859bcce7

[2]https://www.imooc.com/article/273653

[3]https://blog.csdn.net/qq_41475058/article/details/89516051

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM