Nginx 重寫規則指南

jopen 9年前發布 | 17K 次閱讀 Nginx Web服務器

當運維遇到要重寫情況時,往往是要程序員把重寫規則寫好后,發給你,你再到生產環境下配置。對于重寫規則說到底就是正則匹配,做運維的豈能對正則表達式不了解的?最起碼最基本的正則表達式會寫。套用一句阿里的話(某網友說是阿里說的,不清楚到底是不是出自阿里)“不懂程序的運維,不是好運維;不懂運維的開發,不是好開發。”。 正則表達式也是一門語言哈。當你學習一門語言時,必然會遇到該門語言的正則表達式這章節的。 在這里推薦一本非常好的正則表達式書,包含常用的語言的正則寫法如sed、perl、bash、awk、php、c#、java、javascript、python、ruby等等,《Regular Expressions Cookbook, 2nd Edition》,也有中文版的,大家可以到網絡上找找。

本文介紹nginx的重寫模塊,創建重寫規則向導,便于快捷正確的創建新的重寫規則,不求救于人。同時,如果想把apache轉換成nginx,重寫規則也是要改的咯。

一. rewrite模塊介紹

nginx的重寫模塊是一個簡單的正則表達式匹配與一個虛擬堆疊機結合。依賴于PCRE庫,因此需要安裝pcre。根據相關變量重定向和選擇不同的配置,從一個location跳轉到另一個location,不過這樣的循環最多可以執行10次,超過后nginx將返回500錯誤。同時,重寫模塊包含set指令,來創建新的變量并設其值,這在有些情景下非常有用的,如記錄條件標識、傳遞參數到其他location、記錄做了什么等等。

二. rewrite模塊指令

break

語法:break

默認值:none

使用字段:server, location, if

完成當前設置的重寫規則,停止執行其他的重寫規則。

if

語法:if (condition) { … }

默認值:none

使用字段:server, location

注意:盡量考慮使用try_files代替。判斷的條件可以有以下值:

  1. 一個變量的名稱:空字符傳“”或者一些“0”開始的字符串為false。
  2. 字符串比較:使用=或!=運算符
  3. 正則表達式匹配:使用~(區分大小寫)和~*(不區分大小寫),取反運算!~和!~*。
  4. 文件是否存在:使用-f和!-f操作符
  5. 目錄是否存在:使用-d和!-d操作符
  6. 文件、目錄、符號鏈接是否存在:使用-e和!-e操作符
  7. 文件是否可執行:使用-x和!-x操作符

return

語法:return code

默認值:none

使用字段:server, location, if

停止處理并為客戶端返回狀態碼。非標準的444狀態碼將關閉連接,不發送任何響應頭。可以使用的狀態碼有:204,400,402-406,408,410, 411, 413, 416與500-504。如果狀態碼附帶文字段落,該文本將被放置在響應主體。相反,如果狀態碼后面是一個URL,該URL將成為location頭補值。沒有狀態碼的URL將被視為一個302狀態碼。

rewrite

語法:rewrite regex replacement flag

默認值:none

使用字段:server, location, if

按照相關的正則表達式與字符串修改URI,指令按照在配置文件中出現的順序執行。可以在重寫指令后面添加標記。

注意:如果替換的字符串以http://開頭,請求將被重定向,并且不再執行多余的rewrite指令。

尾部的標記(flag)可以是以下的值:

  • last – 停止處理重寫模塊指令,之后搜索location與更改后的URI匹配。
  • break – 完成重寫指令。
  • redirect – 返回302臨時重定向,如果替換字段用http://開頭則被使用。
  • permanent – 返回301永久重定向。

rewrite_log

語法:rewrite_log on | off

默認值:rewrite_log off

使用字段:server, location, if

變量:無

啟用時將在error log中記錄notice級別的重寫日志。

set

語法:set variable value

默認值:none

使用字段:server, location, if

為給定的變量設置一個特定值。

uninitialized_variable_warn

語法:uninitialized_variable_warn on|off

默認值:uninitialized_variable_warn on

使用字段:http, server, location, if

控制是否記錄未初始化變量的警告信息。

三. 重寫規則組成部分

3.1 任何重寫規則的第一部分都是一個正則表達式

可以使用括號來捕獲,后續可以根據位置來將其引用,位置變量值取決于捕獲正則表達式中的順序,$1引用第一個括號中的值,$2引用第二個括號中的值,以此類推。如:

^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$

$1是兩個小寫字母組成的字符串,$2是由小寫字母和0到9的數字組成的5個字符的字符串,$3將是個文件名,$4是png、jpg、gif中的其中一個。

3.2 重寫規則的第二部分是URI

請求被改寫。該URI可能包含正則表達式中的捕獲的位置參數或這個級別下的nginx任何配置變量。如:

/data?file=$3.$4

如果這個URI不匹配nginx配置的任何location,那么將給客戶端返回301(永久重定向)或302(臨時重定向)的狀態碼來表示重定向類型。該狀態碼可以通過第三個參數來明確指定。

3.3 重寫規則的第三部分

第三部分也就是尾部的標記(flag)。 last標記將導致重寫后的URI搜索匹配nginx的其他location,最多可循環10次。如:

rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4 last;

break指令可以當做自身指令。如:

if ($bwhog) {
    limit_rate 300k;
    break;
}

另一個停止重寫模塊處理指令是return, 來控制主HTTP模塊處理請求。 這意味著,nginx直接返回信息給客戶端,與error_page結合為客戶端呈現格式化的HTML頁面或激活不同的模塊來完成請求。如果狀態碼附帶文字段落,該文本將被放置在響應主體。相反,如果狀態碼后面是一個URL,該URL將成為location頭補值。沒有狀態碼的URL將被視為一個302狀態碼。如:

location = /image404.html {
    return 404 "image not found\n";
}

四. 實例

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 main;
            root /data/images;
            # 應用前面定義的變量。判斷首先文件在不在,不在再判斷目錄在不在,如果還不在就跳轉到最后一個url里
            try_files /$arg_file /image404.html;
        }
        location = /image404.html {
            # 圖片不存在返回特定的信息
            return 404 "image not found\n";
        }
}

五. 創建新的重新規則

在接到要創建新的重寫規則時,要弄清楚需求是什么樣的,再決定怎么做。畢竟重寫也是耗資源的有效率之分的。 下面的這些問題有些幫助的:

  1. 你的URL的模式是什么樣的?
  2. 是否有一個以上的方法來實現?
  3. 是否需要捕獲URL部分作為變量?
  4. 重定向到另一個web上可以看到我的規則?
  5. 是否要替換查詢的字符串參數?

檢查網站或應用程序布局,清楚URL模式。啰嗦一句:我一而再再而三的強調,運維不能與開發脫節,運維要參與到開發當中。如果有不止一種方法實現,創建一個永久重定向。同時,定義一個重寫規范,來使網址清潔,還可以幫助網站更容易被找到。

實例1. 要將home目錄重定向到主頁面上,目錄結構如下:

/
/home
/home/
/home/index
/home/index/
/index
/index.php
/index.php/

重寫規則如下:

rewrite ^/(home(/index)?|index(\.php)?)/?$ $scheme:
//$host/ permanent;

指定$scheme和$host變量,因為要做一個永久重定向并希望nginx使用相同的參數來構造URL。

實例2. 如果想分別記錄各個部分的URL,可以使用正則表達式來捕獲URI,然后,給變量分配指定位置變量,見上面的實例。

實例3. 當重寫規則導致內部重定向或指示客戶端調用該規則本身被定義的location時,必須采取特殊的動作來避免重寫循環。如:在server配置段定義了一條規則帶上last標志,在引用location時,必須使用break標志。

server {
    rewrite ^(/images)/(.*)\.(png|jpg|gif)$ $1/$3/$2.$3 last;
    location /images/ {
        rewrite ^(/images)/(.*)\.(png|jpg|gif)$ $1/$3/$2.$3 break;
    }
}

實例4. 作為重寫規則的一部分,傳遞新的查詢字符串參數是使用重寫規則的目標之一。 如:

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

nginx重寫規則說起來挺簡單的,做起來就難,重點在于正則表達式,同時,還需要考慮到nginx執行順序。  

原文:http://www.ttlsa.com/nginx/nginx-rewriting-rules-guide/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!