nginx rewrite規則語法

最近在VPS上嘗試配置安裝一個網站,VPS安裝了LNMP(Linux+Nginx+MySQL+php)在配置重定規則的時候常常遇到一些問題,直接用Apache的規則到Nginx下沒起做用。原來Apache 重寫的規則到nginx上還有一些不太同樣的地方。

這裏只是簡單記錄一些學習示例,高手略過,新手能夠看一下。php

Nginx Rewrite規則相關指令 
Nginx Rewrite規則相關指令有if、rewrite、set、return、break等,其中rewrite是最關鍵的指令。一個簡單的Nginx Rewrite規則語法以下:css

rewrite ^/b/(.*)\.html /play.php?video=$1 break;

若是加上if語句,示例以下:html

if (!-f $request_filename)
rewrite ^/img/(.*)$ /site/$host/p_w_picpaths/$1 last;

Nginx與Apache的Rewrite規則實例對比nginx

簡單的Nginx和Apache 重寫規則區別不大,基本上可以徹底兼容。例如:web

Apache Rewrite 規則:正則表達式

RewriteRule ^/(mianshi|xianjing)/$ /zl/index.php?name=$1 [L]
RewriteRule ^/ceshi/$ /zl/ceshi.php [L]
RewriteRule ^/(mianshi)_([a-zA-Z]+)/$ /zl/index.php?name=$1_$2 [L]
RewriteRule ^/pingce([0-9]*)/$ /zl/pingce.php?id=$1 [L]

Nginx Rewrite 規則:
sql

rewrite ^/(mianshi|xianjing)/$ /zl/index.php?name=$1 last;
rewrite ^/ceshi/$ /zl/ceshi.php last;
rewrite ^/(mianshi)_([a-zA-Z]+)/$ /zl/index.php?name=$1_$2 last;
rewrite ^/pingce([0-9]*)/$ /zl/pingce.php?id=$1 last;

由以上示例能夠看出,Apache的Rewrite規則改成Nginx的Rewrite規則,其實很簡單:Apache的RewriteRule指令換成Nginx的rewrite指令,Apache的[L]標記換成Nginx的last標記,中間的內容不變。apache

若是Apache的Rewrite規則改成Nginx的Rewrite規則後,使用nginx -t命令檢查發現nginx.conf配置文件有語法錯誤,那麼能夠嘗試給條件加上引號。例如一下的Nginx Rewrite規則會報語法錯誤:瀏覽器

rewrite ^/([0-9]{5}).html$ /x.jsp?id=$1 last;

加上引號就正確了:
緩存

rewrite 「^/([0-9]{5}).html$」 /x.jsp?id=$1 last;

Apache與Nginx的Rewrite規則在URL跳轉時有細微的區別:

Apache Rewrite 規則:

RewriteRule ^/html/tagindex/([a-zA-Z]+)/.*$ /$1/ [R=301,L]

Nginx Rewrite 規則:

rewrite ^/html/tagindex/([a-zA-Z]+)/.*$ http://$host/$1/ permanent;

以上示例中,咱們注意到,Nginx Rewrite 規則的置換串中增長了「http://$host」,這是在Nginx中要求的。

另外,Apache與Nginx的Rewrite規則在變量名稱方面也有區別,例如:

Apache Rewrite 規則:

RewriteRule ^/user/login/$ /user/login.php?login=1&forward=http://%{HTTP_HOST} [L]

Nginx Rewrite 規則:

rewrite ^/user/login/$ /user/login.php?login=1&forward=http://$host   last;

Apache與Nginx Rewrite 規則的一些功能相同或相似的指令、標記對應關係:

Apache的RewriteCond指令對應Nginx的if指令;
Apache的RewriteRule指令對應Nginx的rewrite指令;
Apache的[R]標記對應Nginx的redirect標記;
Apache的[P]標記對應Nginx的last標記;
Apache的[R,L]標記對應Nginx的redirect標記;
Apache的[P,L]標記對應Nginx的last標記;
Apache的[PT,L]標記對應Nginx的last標記;

容許指定的域名訪問本站,其餘域名一概跳轉到http://www.aaa.com

Apache Rewrite 規則:

RewriteCond %{HTTP_HOST}    ^(.*?)\.domain\.com$
RewriteCond %{HTTP_HOST}    !^qita\.domain\.com$
RewriteCond %{DOCUMENT_ROOT}/market/%1/index.htm -f
RewriteRule ^/wu/$ /market/%1/index.htm [L]

Nginx的if指令不支持嵌套,也不支持AND、OR等多條件匹配,相比於Apache的RewriteCond,顯得麻煩一些,可是,咱們能夠經過下一頁的Nginx配置寫法來實現這個示例:

Nginx Rewrite 規則:

if ($host ~* ^(.*?)\.domain\.com$) set $var_wupin_city $1;     set $var_wupin ‘1′;
if ($host ~* ^qita\.domain\.com$)
    set $var_wupin ‘0′;
if (!-f $document_root/market/$var_wupin_city/index.htm)
     set $var_wupin ‘0′;
if ($var_wupin ~ ‘1′)
    rewrite ^/wu/$ /market/$var_wupin_city/index.htm last;}


rewrite 的語法

用法: rewrite 正則 替換 標誌位

做用域: server, location, if

這個指令根據表達式來更改URI,或者修改字符串。指令根據配置文件中的順序來執行。

注意重寫表達式只對相對路徑有效。若是你想配對主機名,你應該使用if語句。

nginx的rewrite規則參考:

  • set

  • if

  • return

  • break

  • rewrite

麻雀雖小,五臟俱全.只是簡單的幾個指令卻能夠作出絕對不輸apache的簡單靈活的配置.

1.set  主要是用來設置變量用的,沒什麼特別的


2.if  主要用來判斷一些在rewrite語句中沒法直接匹配的條件,好比檢測文件存在與否,http header,cookie等,

   用法: if(條件) {…}

- 當if表達式中的條件爲true,則執行if塊中的語句

- 當表達式只是一個變量時,若是值爲空或者任何以0開頭的字符串都會看成false

- 直接比較內容時,使用 = 和 !=

- 使用正則表達式匹配時,使用

~ 大小寫敏感匹配 
~* 大小寫不敏感匹配 
!~ 大小寫敏感不匹配 
!~* 大小寫不敏感不匹配

隨便一提,由於nginx使用花括號{}判斷區塊,因此當正則中包含花括號時,則必須用雙引號將正則包起來.對下面講到的rewrite語句中的正則亦是如此. 
好比 「\d{4}\d{2}\.+」

- 使用-f,-d,-e,-x檢測文件和目錄,跟~相似,前置!則爲」非」操做

-f 檢測文件存在
-d 檢測目錄存在
-e 檢測文件,目錄或者符號連接存在
-x 檢測文件可執行

舉例:

if ( $http_user_agent ~ MSIE ) { rewrite ^ (. * )$ /msie / $1 break; }

//若是UA包含」MSIE」,rewrite 請求到/msie目錄下

if ( $http_cookie ~ * "id=([^;] +)(?:;|$)" ) { set $id $1;}

//若是cookie匹配正則,設置變量$id等於正則引用部分

if ( $request_method = POST ) { return 405; }

//若是提交方法爲POST,則返回狀態405 (Method not allowed)

if ( !-f $request_filename ) { break; proxy_pass http://127.0.0.1; }

//若是請求文件名不存在,則反向代理localhost

if ( $args ~ post=140 ) { rewrite ^ http: //example.com / permanent;}

//若是query string中包含」post=140″,永久重定向到example.com


3.return  可用來直接設置HTTP返回狀態,好比403,404等(301,302不可用return返回,這個下面會在rewrite提到)


4.break  當即中止rewrite檢測,跟下面講到的rewrite的break flag功能是同樣的,區別在於前者是一個語句,後者是rewrite語句的flag


5.rewrite  最核心的功能(廢話)

其中標誌位有四種

break – 中止rewrite檢測,也就是說當含有break flag的rewrite語句被執行時,該語句就是rewrite的最終結果 
last – 中止rewrite檢測,可是跟break有本質的不一樣,last的語句不必定是最終結果,這點後面會跟nginx的location匹配一塊兒提到
redirect – 返回302臨時重定向,通常用於重定向到完整的URL(包含http:部分) 
permanent – 返回301永久重定向,通常用於重定向到完整的URL(包含http:部分)

由於301和302不能簡單的只單純返回狀態碼,還必須有重定向的URL,這就是return指令沒法返回301,302的緣由了. 做爲替換,rewrite能夠更靈活的使用redirect和permanent標誌實現301和302. 好比上一篇日誌中提到的Blog搬家要作的域名重定向,在nginx中就會這麼寫

rewrite ^ (.* )$ http://newdomain.com/ permanent;

舉例來講一下rewrite的實際應用

rewrite ^ (/download /* ) /media / (* )\.. *$1 /mp3 / $2.mp3 last;

若是請求爲 /download/eva/media/op1.mp3 則請求被rewrite到 /download/eva/mp3/op1.mp3


要注意的是rewrite有不少潛規則須要注意

- rewrite的生效區塊爲sever, location, if

- rewrite只對相對路徑進行匹配,不包含hostname 好比說以上面301重定向的例子說明

rewrite ~ * cafeneko\.info http://newdomain.com / permanent;

這句是永遠沒法執行的,以這個URL爲例

http://blog.cafeneko.info/2010/10/neokoseseiki_in_new_home/?utm_source=rss&utm_medium=rss&utm_campaign=neokoseseiki_in_new_home

其中cafeneko.info叫作hostname,再日後到?爲止叫作相對路徑,?後面的一串叫作query string

對於rewrite來講,其正則表達式僅對」/2010/10/neokoseseiki_in_new_home」這一部分進行匹配,即不包含hostname,也不包含query string .因此除非相對路徑中包含跟域名同樣的string,不然是不會匹配的. 


若是非要作域名匹配的話就要使用if語句了,好比進行去www跳轉

if ( $host ~ * ^www\. (cafeneko\.info) ) { set $host_without_www $1; rewrite ^ (* )$ http: // $host_without_www $1permanent; }

- 使用相對路徑rewrite時,會根據HTTP header中的HOST跟nginx的server_name匹配後進行rewrite,若是HOST不匹配或者沒有HOST信息的話則rewrite到server_name設置的第一個域名,若是沒有設置server_name的話,會使用本機的localhost進行rewrite

- 前面提到過,rewrite的正則是不匹配query string的,因此默認狀況下,query string是自動追加到rewrite後的地址上的,若是不想自動追加query string,則在rewrite地址的末尾添加?

rewrite ^ /users/ (* )/show? user$1last;

rewrite的基本知識就是這麼多..但尚未完..還有最頭疼的部分沒有說…

/2 Nginx location 和 rewrite retry

nginx的rewrite有個很奇特的特性 — rewrite後的url會再次進行rewrite檢查,最多重試10次,10次後尚未終止的話就會返回HTTP 500


last和break最大的不一樣在於

- break是終止當前location的rewrite檢測,並且再也不進行location匹配 
– last是終止當前location的rewrite檢測,但會繼續重試location匹配並處理區塊中的rewrite規則

仍是這個該死的例子

location /download/ { rewrite ^ ( /download /* ) /media / (* )\.. *$1 /mp3 / $2.mp3 ; rewrite ^ ( /download /* )/movie / (* )\.. *$1 /avi / $2.mp3 ; rewrite ^ ( /download /* ) /avvvv / (* )\.. *$1 /rmvb / $2.mp3 ; }

上面沒有寫標誌位,請各位自行腦補…

若是請求爲 /download/acg/moive/UBW.avi

last的狀況是: 在第2行rewrite處終止,並重試location /download..死循環 
break的狀況是: 在第2行rewrite處終止,其結果爲最終的rewrite地址.


也就是說,上面的某位試圖下載eva op不但沒下到反而被HTTP 500射了一臉的例子正是由於用了last標誌因此纔會形成死循環,若是用break就沒事了.

location /download/ { rewrite ^ ( /download /* ) /media / (* )\.. *$1 /mp3 / $2.mp3 break}

對於這個問題,我我的的建議是,若是是全局性質的rewrite,最好放在server區塊中並減小沒必要要的location區塊.location區塊中的rewrite要想清楚是用last仍是break.

有些狀況是要用last的. 典型的例子就是wordpress的permalink rewrite

常見的狀況下, wordpress的rewrite是放在location /下面,並將請求rewrite到/index.php

這時若是這裏使用break乃就掛了,不信試試. b( ̄▽ ̄)d…由於nginx返回的是沒有解釋的index.php的源碼…

這裏必定要使用last才能夠在結束location / 的rewrite, 並再次命中location ~ \.php$,將其交給fastcgi進行解釋.其後返回給瀏覽器的纔是解釋過的html代碼.

關於nginx rewrite的簡介到這裏就所有講完了,水平及其有限,請你們指出錯漏…



結合QeePHP的例子

if (!-d $request_filename) {
    rewrite ^/([a-z-A-Z]+)/([a-z-A-Z]+)/?(.*)$ /index.php?namespace=user&controller=$1&action=$2&$3 last;
    rewrite ^/([a-z-A-Z]+)/?$ /index.php?namespace=user&controller=$1 last;
    break;  
}

多目錄轉成參數
abc.domian.com/sort/2 => abc.domian.com/index.php?act=sort&name=abc&id=2

if ($host ~* (.*)\.domain\.com) {
    set $sub_name $1;
    rewrite ^/sort\/(\d+)\/?$ /index.php?act=sort&cid=$sub_name&id=$1 last;
}

目錄對換
/123456/xxxx -> /xxxx?id=123456

rewrite ^/(\d+)/(.+)/ /$2?id=$1 last;

例以下面設定nginx在用戶使用ie的使用重定向到/nginx-ie目錄下:

if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$ /nginx-ie/$1 break;
}

目錄自動加「/」

if (-d $request_filename){
    rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent;
}

禁止htaccess

location ~/\.ht {
    deny all;
}

禁止多個目錄

location ~ ^/(cron|templates)/ {
    deny all;
    break;
}

禁止以/data開頭的文件
能夠禁止/data/下多級目錄下.log.txt等請求;

location ~ ^/data {
    deny all;
}

禁止單個目錄
不能禁止.log.txt能請求

location /searchword/cron/ {
    deny all;
}

禁止單個文件

location ~ /data/sql/data.sql {
deny all;
}

給favicon.ico和robots.txt設置過時時間;
這裏爲favicon.ico爲99天,robots.txt爲7天並不記錄404錯誤日誌

location ~(favicon.ico) {
    log_not_found off;
    expires 99d;
    break;
}
location ~(robots.txt) {
    log_not_found off;
    expires 7d;
    break;
}

設定某個文件的過時時間;這裏爲600秒,並不記錄訪問日誌

location ^~ /html/scripts/loadhead_1.js {
    access_log   off;
    root /opt/lampp/htdocs/web;
    expires 600;
    break;
}

文件反盜鏈並設置過時時間
這裏的return 412 爲自定義的http狀態碼,默認爲403,方便找出正確的盜鏈的請求
「rewrite ^/ http://leech.divmy.com/leech.gif;」顯示一張防盜鏈圖片
「access_log off;」不記錄訪問日誌,減輕壓力
「expires 3d」全部文件3天的瀏覽器緩存

location ~* ^.+\.(jpg|jpeg|gif|png|swf|rar|zip|css|js)$ {
    valid_referers none blocked *.c1gstudio.com *.c1gstudio.net localhost 208.97.167.194;
    if ($invalid_referer) {
        rewrite ^/ http://leech.divmy.com/leech.gif;
        return 412;
        break;
    }
    access_log   off;
    root /opt/lampp/htdocs/web;
    expires 3d;
    break;
}

只充許固定ip訪問網站,並加上密碼

root  /opt/htdocs/www;
    allow   208.97.167.194;
    allow   222.33.1.2;
    allow   231.152.49.4;
    deny    all;
    auth_basic 「C1G_ADMIN」;
    auth_basic_user_file htpasswd;

將多級目錄下的文件轉成一個文件,加強seo效果
/job-123-456-789.html 指向/job/123/456/789.html

rewrite ^/job-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /job/$1/$2/jobshow_$3.html last;

將根目錄下某個文件夾指向2級目錄
如/shanghaijob/ 指向 /area/shanghai/
若是你將last改爲permanent,那麼瀏覽器地址欄顯是/location/shanghai/

rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;

上面例子有個問題是訪問/shanghai 時將不會匹配

rewrite ^/([0-9a-z]+)job$ /area/$1/ last;
rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;

這樣/shanghai 也能夠訪問了,但頁面中的相對連接沒法使用,
如./list_1.html真實地址是/area/shanghia/list_1.html會變成/list_1.html,導至沒法訪問。

那我加上自動跳轉也是不行咯
(-d $request_filename)它有個條件是必需爲真實目錄,而個人rewrite不是的,因此沒有效果

if (-d $request_filename){
    rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent;
}

知道緣由後就好辦了,讓我手動跳轉吧

rewrite ^/([0-9a-z]+)job$ /$1job/ permanent;
rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;

文件和目錄不存在的時候重定向:

if (!-e $request_filename) {
    proxy_pass http://127.0.0.1;
}

域名跳轉

server
{
listen       80;
server_name  jump.88dgw.com;
index index.html index.htm index.php;
root  /opt/lampp/htdocs/www;
rewrite ^/ http://www.88dgw.com/;
access_log  off;
}

多域名轉向

server_name  www.7oom.com/  www.divmy.com/;
index index.html index.htm index.php;
root  /opt/lampp/htdocs;
if ($host ~ 「c1gstudio\.net」) {
    rewrite ^(.*) http://www.7oom.com$1/ permanent;
}

三級域名跳轉

if ($http_host ~* 「^(.*)\.i\.c1gstudio\.com$」) {
    rewrite ^(.*) http://top.88dgw.com$1/;
    break;
}

域名鏡向

server
{
listen       80;
server_name  mirror.c1gstudio.com;
index index.html index.htm index.php;
root  /opt/lampp/htdocs/www;
rewrite ^/(.*) http://www.divmy.com/$1 last;
access_log  off;
}

某個子目錄做鏡向

location ^~ /zhaopinhui {
rewrite ^.+ http://zph.divmy.com/ last;
break;
}

discuz ucenter home (uchome) rewrite

rewrite ^/(space|network)-(.+)\.html$ /$1.php?rewrite=$2 last;
rewrite ^/(space|network)\.html$ /$1.php last;
rewrite ^/([0-9]+)$ /space.php?uid=$1 last;

discuz 7 rewrite

rewrite ^(.*)/archiver/((fid|tid)-[\w\-]+\.html)$ $1/archiver/index.php?$2 last;
rewrite ^(.*)/forum-([0-9]+)-([0-9]+)\.html$ $1/forumdisplay.php?fid=$2&page=$3 last;
rewrite ^(.*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/viewthread.php?tid=$2&extra=page\%3D$4&page=$3 last;
rewrite ^(.*)/profile-(username|uid)-(.+)\.html$ $1/viewpro.php?$2=$3 last;
rewrite ^(.*)/space-(username|uid)-(.+)\.html$ $1/space.php?$2=$3 last;
rewrite ^(.*)/tag-(.+)\.html$ $1/tag.php?name=$2 last;

給discuz某版塊單獨配置域名

server_name  bbs.c1gstudio.com news.c1gstudio.com;
location = / {
if ($http_host ~ news\.divmy.com$) {
    rewrite ^.+ http://news.divmy.com/forum-831-1.html last;
    break;
    }
}

discuz ucenter 頭像 rewrite 優化

location ^~ /ucenter {
location ~ .*\.php?$
{
#fastcgi_pass  unix:/tmp/php-cgi.sock;
fastcgi_pass  127.0.0.1:9000;
fastcgi_index index.php;
include fcgi.conf;
}
location /ucenter/data/avatar {
log_not_found off;
access_log   off;
location ~ /(.*)_big\.jpg$ {
    error_page 404 /ucenter/p_w_picpaths/noavatar_big.gif;
}
location ~ /(.*)_middle\.jpg$ {
    error_page 404 /ucenter/p_w_picpaths/noavatar_middle.gif;
}
location ~ /(.*)_small\.jpg$ {
    error_page 404 /ucenter/p_w_picpaths/noavatar_small.gif;
}
expires 300;
break;
}
}

jspace rewrite

location ~ .*\.php?$
{
#fastcgi_pass  unix:/tmp/php-cgi.sock;
fastcgi_pass  127.0.0.1:9000;
fastcgi_index index.php;
include fcgi.conf;
}
location ~* ^/index.php/
{
rewrite ^/index.php/(.*) /index.php?$1 break;
fastcgi_pass  127.0.0.1:9000;
fastcgi_index index.php;
include fcgi.conf;
}

另外這裏還有一個工具能夠直接把apache規則轉化爲nginx規則

http://www.anilcetin.com/convert-apache-htaccess-to-nginx/

參考:

http://wiki.nginx.org/NginxChsHttpRewriteModule

http://blog.csdn.net/cnbird2008/archive/2009/08/04/4409620.aspx


http://www.divmy.com/

相關文章
相關標籤/搜索