前端開發掌握nginx常用功能之rewrite

上一篇博文對nginx最常用功能的server及location的匹配規則進行了講解,這也是nginx實現控制訪問和反向代理的基礎。掌握請求的匹配規則算是對nginx有了入門,但是這些往往還是不能滿足實際的需求場景,例如請求url重寫、重定向等等,這都需要對請求的path進行修改操作的,匹配規則是不能獨自完成實際需求的,這就需要掌握nginx的另一個常用功能rewrite,下面就來說說這個常用功能。

Rewrite規則

rewrite功能就是,使用nginx提供的全局變量或自己設置的變量,結合正則表達式和標志位實現url重寫以及重定向。
rewrite只能放在server{}, location{}, if{} 中,并且只能對域名后邊傳遞的參數外的字符串起作用,例如 http://baidu.com/a/we/index.php?id=1&u=str 只對/a/we/index.php重寫。語法:

rewrite regex replacement [flag];

如果相對域名或參數字符串起作用,可以使用全局變量匹配,也可以使用proxy_pass反向代理。

表面上看rewrite和location功能有點像,都能實現跳轉,主要區別在于rewrite是在同一域名內更改獲取資源的路徑,而location是對一類路徑做控制訪問或反向代理,可以proxy_pass到其他機器。很多情況下rewrite也會寫在location里,它們的執行順序是:

  • 執行server塊的rewrite指令
  • 執行location匹配
  • 執行選定的location中的rewrite指令

如果其中某步URI被重寫,則重新循環執行1-3,直到找到真實存在的文件;循環超過10次,則返回500 Internal Server Error錯誤。

2.1 flag標志位

  • last : 停止執行當前ngx_http_rewrite_module的指令集,但是會繼續走一遍請求匹配對應server或者location;
  • break : 停止執行當前ngx_http_rewrite_module的指令集,請求就此完成。
  • redirect : 返回302臨時重定向,地址欄會顯示跳轉后的地址
  • permanent : 返回301永久重定向,地址欄會顯示跳轉后的地址

因為301和302不能簡單的只返回狀態碼,還必須有重定向的URL,這就是return指令無法返回301,302的原因了。

對于上面的flag,有幾點需要強調一下:

  • lastbreak對url的重寫不會改變地址欄的地址

也就是說,nginx雖然對請求url進行了重寫,但是地址欄不會有任何明顯的改變,仍然顯示nginx重寫前的地址;這與redirectpermanent不同。

  • lastbreak的處理策略不同

二者都會終止當前ngx_http_rewrite_module的指令集的執行,但是 last 立即發起新一輪的 請求匹配 而 break 則不會。

  • redirectpermanent會終止后續nginx指令的執行

nginx在rewrite遇到flag是二者時,后續的指令是不會執行的。

server {listen 8080;location = /test {break;return 200 $request_uri;proxy_pass http://127.0.0.1:8080/other;}location / {return 200 $request_uri;}
}

上面例子中,我們訪問 curl 127.0.0.1:8080/test,會發現,return 200 $request_uri語句沒有執行,而proxy_pass指令被執行了。這是因為:

return指令屬于ngx_http_proxy_module模塊,它會被break終止掉;而rewrite模塊它是ngx_http_proxy_module的指令,不會被break給中斷掉。

2.2 if指令與全局變量

if判斷指令
語法為if(condition){...} ,對給定的條件condition進行判斷。如果為真,大括號內的rewrite指令將被執行,if條件(conditon)可以是如下任何內容:

  • 當表達式只是一個變量時,如果值為空或任何以0開頭的字符串都會當做false
  • 直接比較變量和內容時,使用=!=
  • ~正則表達式匹配,~*不區分大小寫的匹配,!~區分大小寫的不匹配

-f!-f用來判斷是否存在文件
-d!-d用來判斷是否存在目錄
-e!-e用來判斷是否存在文件或目錄
-x!-x用來判斷文件是否可執行

例如:

if ($http_user_agent ~ MSIE) {rewrite ^(.*)$ /msie/$1 break;
} //如果UA包含"MSIE",rewrite請求到/msid/目錄下if ($http_cookie ~* "id=([^;]+)(?:;|$)") {set $id $1;} //如果cookie匹配正則,設置變量$id等于正則引用部分if ($request_method = POST) {return 405;
} //如果提交方法為POST,則返回狀態405(Method not allowed)。return不能返回301,302if ($slow) {limit_rate 10k;
} //限速,$slow可以通過 set 指令設置if (!-f $request_filename){break;proxy_pass  http://127.0.0.1;
} //如果請求的文件名不存在,則反向代理到localhost 。這里的break也是停止rewrite檢查if ($args ~ post=140){rewrite ^ http://example.com/ permanent;
} //如果query string中包含"post=140",永久重定向到example.comlocation ~* \.(gif|jpg|png|swf|flv)$ {valid_referers none blocked www.jefflei.com www.leizhenfang.com;if ($invalid_referer) {return 404;} //防盜鏈
}

全局變量

下面是可以用作if判斷的全局變量:

  • $args : #這個變量等于請求行中的參數,同$query_string
  • $content_length : 請求頭中的Content-length字段。
  • $content_type : 請求頭中的Content-Type字段。
  • $document_root : 當前請求在root指令中指定的值。
  • $host : 請求主機頭字段,否則為服務器名稱。
  • $http_user_agent : 客戶端agent信息
  • $http_cookie : 客戶端cookie信息
  • $limit_rate : 這個變量可以限制連接速率。
  • $request_method : 客戶端請求的動作,通常為GET或POST。
  • $remote_addr : 客戶端的IP地址。
  • $remote_port : 客戶端的端口。
  • $remote_user : 已經經過Auth Basic Module驗證的用戶名。
  • $request_filename : 當前請求的文件路徑,由root或alias指令與URI請求生成。
  • $scheme : HTTP方法(如http,https)。
  • $server_protocol : 請求使用的協議,通常是HTTP/1.0或HTTP/1.1。
  • $server_addr : 服務器地址,在完成一次系統調用后可以確定這個值。
  • $server_name : 服務器名稱。
  • $server_port : 請求到達服務器的端口號。
  • $request_uri : 包含請求參數的原始URI,不包含主機名,如:”/foo/bar.php?arg=baz”。
  • $uri : 不帶請求參數的當前URI,$uri不包含主機名,如”/foo/bar.html”。
  • $document_uri : 與$uri相同。

例如:

例:http://localhost:88/test1/test2/test.php
$host:localhost
$server_port:88
$request_uri:http://localhost:88/test1/test2/test.php
$document_uri:/test1/test2/test.php
$document_root:/var/www/html
$request_filename:/var/www/html/test1/test2/test.php

2.3 常用正則

  • . : 匹配除換行符以外的任意字符
  • ? : 重復0次或1次
  • + : 重復1次或更多次
  • * : 重復0次或更多次
  • \d :匹配數字
  • ^ : 匹配字符串的開始
  • $ : 匹配字符串的結束
  • {n} : 重復n次
  • {n,} : 重復n次或更多次
  • [c] : 匹配單個字符c
  • [a-z] : 匹配a-z小寫字母的任意一個

小括號()之間匹配的內容,可以在后面通過$1來引用,$2表示的是前面第二個()里的內容。正則里面容易讓人困惑的是\轉義特殊字符。

2.4 rewrite實例

例1:

http {# 定義image日志格式log_format imagelog '[$time_local] ' $image_file ' ' $image_type ' ' $body_bytes_sent ' ' $status;# 開啟重寫日志rewrite_log on;server {root /home/www;location / {# 重寫規則信息error_log logs/rewrite.log notice;# 注意這里要用‘’單引號引起來,避免{}rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4;# 注意不能在上面這條規則后面加上“last”參數,否則下面的set指令不會執行set $image_file $3;set $image_type $4;}location /data {# 指定針對圖片的日志格式,來分析圖片類型和大小access_log logs/images.log mian;root /data/images;# 應用前面定義的變量。判斷首先文件在不在,不在再判斷目錄在不在,如果還不在就跳轉到最后一個url里try_files /$arg_file /image404.html;}location = /image404.html {# 圖片不存在返回特定的信息return 404 "image not found\n";}
}

對形如/images/ef/uh7b3/test.png的請求,重寫到/data?file=test.png,于是匹配到location /data,先看/data/images/test.png文件存不存在,如果存在則正常響應,如果不存在則重寫tryfiles到新的image404 location,直接返回404狀態碼。

例2:

rewrite ^/images/(.*)_(\d+)x(\d+)\.(png|jpg|gif)$ /resizer/$1.$4?width=$2&height=$3? last;

對形如/images/bla_500x400.jpg的文件請求,重寫到/resizer/bla.jpg?width=500&height=400地址,并會繼續嘗試匹配location。

例3:
見 ssl部分頁面加密 。

2.5 rewrite需要注意的問題

上面說過,rewrite的指令規則為:rewrite regex replacement [flag];

rewrite指令用指定的regex來匹配請求的uri,若匹配成功則用replacement來重寫請求uri。這里需要注意的replacement字符串的內容:

1、 若replacement以http://https://或者$scheme開頭,則告訴nginx這是重定向操作(flag默認為redirect),nginx則停止處理后續內容,并直接重定向返回給客戶端。

location / {# 當匹配 正則表達式 /test/(.*)時 請求將被臨時重定向到 http://www.baidu.com/$1# flag默認為redirectrewrite /test/(.*) https://www.baidu.com/$1;return 200 ’ok'; # 此處沒有機會執行
}

2、replacement非以上三種情況開頭,則就是簡單的url重寫

location / {# 當匹配 正則表達式 /test/(.*)時 請求將被臨時重定向到 www.baidu.com/$1# flag無值則rewrite會順序執行rewrite /test/(.*) www.baidu.com/$1;return 200 ’ok'; # 此處因為rewrite順序執行而得到執行機會
}

對于上面兩種情況,還需要特別留意一個redirect端口的問題,先上一個例子:

## server.com機器上nginx的配置如下:
server {listen 8000;location /test1/ {rewrite /test1/index.html http://server1.com/demo/test1 redirect;}location /test2/ {rewrite /test2/index.html /demo/test2 redirect;proxy_pass http://192.168.1.3:8000;}
}

當訪問http://server.com/test1/index.html時,會命中/test1的location規則,訪問server1.com對應內容一直失敗,發現重定向后響應頭的Location字段值為http://server1.com:8000/demo/test1,帶有8000端口,我們并沒有配置,表現的比較詭異?

訪問http://server.com/test2/index.html時,命中/test2的location規則,同樣訪問失敗,但是訪問的重定向后響應頭Location字段值為http://server.com:8000/demo/test2,其帶有server.com的server_name和8000的端口,更加詭異?

看到上面的現象,疑惑重重;其實這跟nginx的server_name_in_redirectport_in_redirect指令有關:

在絕對路徑中,server_name_in_redirectport_in_redirect 指令表示是否將server塊中的 server_name 和 listen 的端口作為redirect用, 重定向的完整url地址根據$schemeserver_name_in_redirectport_in_redirect來確定的。

在絕對路徑中,server_name_in_redirect默認是禁用的,而port_in_redirect是默認啟用的。對于帶有$scheme重定向的絕對路徑,nginx會從replacement中獲取指定的server_name和port來進行重定向:

第一種,若replacement帶請求協議http(s),而其中沒有指定port的話,nginx會默認取當前server的listen端口作為重定向的端口。這是上面訪問http://server.com/test1/index.html時重定向到http://server.com:8000/demo/test2時會攜帶8000的原因。

第二種,若replacement不帶請求協議http(s),而是相對本地服務器的絕對地址的話,如上面訪問http://server.com/test2/index.html的情況,此時server_name_in_redirect由于禁用它會去請求的host來作為server_name,取當前server的listen端口作為重定向的端口,最終重定向到http://server.com:8000/demo/test2

或許你會問,訪問http://server.com/test2/index.html為什么不會重定向到http://192.168.1.3:8000/demo/test2上?這是因為rewrite的redirect flag會終止后續指令的執行,所以其后的proxy_pass指令不會執行。

參考

  • http://www.nginx.cn/216.html
  • http://www.ttlsa.com/nginx/nginx-rewriting-rules-guide/
  • https://segmentfault.com/a/1190000008102599

轉載于:https://www.cnblogs.com/wonyun/p/10355574.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/249675.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/249675.shtml
英文地址,請注明出處:http://en.pswp.cn/news/249675.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue2.0腳手架的webpack 配置文件分析

前言 作為 Vue 的使用者我們對于 vue-cli 都很熟悉,但是對它的 webpack 配置我們可能關注甚少,今天我們為大家帶來 vue-cli#2.0 的 webpack 配置分析 vue-cli 的簡介、安裝我們不在這里贅述,對它還不熟悉的同學可以直接訪問 vue-cli 查看 …

一個可供中小團隊參考的微服務架構技術棧

一個可供中小團隊參考的微服務架構技術棧

WinSxS文件夾瘦身

WinSxS文件夾瘦身2014-5-8 18:03:32來源:IT之家作者:阿象責編:阿象 評論:27剛剛,我們分享了如何用DISM管理工具查看Win8.1 WinSxS文件夾實際大小。對于WinSxS文件夾,幾乎每個Windows愛好者都認識到其重要性…

bcrypt的簡單使用

前段時間在搗鼓個人項目的時候用到了nodejs做服務端,發現使用加密的方法和之前常用的加密方式不太一致,下面以demo的形式總結一下bcrypt對密碼進行加密的方法。 一、簡介 Bcrypt簡介: bcrypt是一種跨平臺的文件加密工具。bcrypt 使用的是布…

盒子居中

1、未脫標 margin:0 auto; 2、脫標(absolute、fixed) left:50%; margin-left:width/2; 轉載于:https://www.cnblogs.com/liujianing/p/10356984.html

織夢無子欄目時禁止調用同級欄目

1. 修改文件 \include\taglib\channel.lib.php 把代碼 if($typeson && $reid!0 && $totalRow0) 改為 if($typeson && $reid!0 && $totalRow0 && $noself) 2. 使用channel標簽時添加noself屬性 {dede:channel noselfyes} {/dede:channe…

nodejs實現文件上傳

前段時間在做個人項目的時候,用到了nodejs服務端上傳文件,現在回頭把這個小結一下,作為記錄。 本人上傳文件時是基于express的multiparty,當然也可以使用connect-multiparty中間件實現,但官方似乎不推薦使用connect-m…

python騰訊語音合成

一、騰訊語音合成介紹 騰訊云語音合成技術(TTS)可以將任意文本轉化為語音,實現讓機器和應用張口說話。 騰訊TTS技術可以應用到很多場景,比如,移動APP語音播報新聞;智能設備語音提醒;依靠網上現有…

鉤子函數和回調函數的區別

一般認為,鉤子函數就是回調函數的一種,其實還是有差異的,差異地方就是:觸發的時機不同。 先說鉤子函數: 鉤子(Hook)概念源于Windows的消息處理機制,通過設置鉤子,應用程…

【bzoj4712】洪水

Portal --> bzoj4712 Description 給你一棵樹,節點從\(1\)到\(n\)編號,每個節點有一個權值,有若干次操作,操作有以下兩種: \((C,x,delta)\):將編號為\(x\)的點的權值改為\(delta\) \((Q,x)\)&#xff1a…

[USACO]地震 (二分答案+最優比率生成樹詳解)

題面:[USACO 2001 OPEN]地震 題目描述: 一場地震把約翰家的牧場摧毀了, 堅強的約翰決心重建家園。 約翰已經重建了N個牧場,現在他希望能修建一些道路把它們連接起來。研究地形之后,約翰發現可供修建的道路有M條。碰巧的…

HTTP協議學習筆記

1.HTTP協議簡介 (1)客戶端連上web服務器后,若想獲得web服務器中的某個web資源,需遵守一定的通訊格式,HTTP協議用于定義客戶端與web服務器通迅的格式。 (2)HTTP是hypertext transfer protocol&…

defer和async的原理與區別

上一篇剛轉載了一篇有關于網站性能優化的文章,其中提及到了頁面的加載和渲染的過程,提到了defer和async的相關區別,但是本人在此之前并沒有深究其中的區別。 defer和async是script標簽的兩個屬性,用于在不阻塞頁面文檔解析的前提…

一些奇妙的線段樹操作

學過數據結構和會做題完全是兩個概念orz 各種各樣的題目都應該見識一下 簡單的目錄: 最大連續長度 吉司機線段樹 線段樹合并/分裂 最大連續長度問題 典型題目:HDU 3911 ($Black$ $And$ $White$) 題目大意:有一個長度為…

微服務實踐沙龍-上海站

微服務的概念最早由Martin Fowler與James Lewis于2014年共同提出,核心思想是圍繞業務能力組織服務,各個微服務可被獨立部署,服務間是松耦合的關系,以及數據和治理的去中心化管理。微服務能夠幫助企業應對業務復雜、頻繁更新以及團…

Spring的refresh()方法調用過程

Spring的refresh()方法調用過程 refresh()是Spring中比較核心的方法,Spring所有的初始化都在這個方法中完成 具體代碼如下 public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this co…

Web數據存儲之localStorage和sessionStorage

Web數據存儲之localStorage和sessionStorage 學習前端以來,自己了解有localStorage和sessionStorage的相關存儲的知識,也有實踐過,但是之前只限于能用的基礎上,但最近看了一本書,深入了解了localStorage和sessionStor…

(四)RabbitMQ消息隊列-服務詳細配置與日常監控管理

(四)RabbitMQ消息隊列-服務詳細配置與日常監控管理 原文:(四)RabbitMQ消息隊列-服務詳細配置與日常監控管理RabbitMQ服務管理 啟動服務:rabbitmq-server -detached【 /usr/local/rabbitmq/sbin/rabbitmq-server -deta…