1. 程式人生 > >Apache URL重寫規則

Apache URL重寫規則

否則 路徑 url重寫 分享圖片 prism via 則表達式 lena 轉義符

1、簡介

  Apached的重寫功能,即是mod_rewrite模塊功能,它是apache的一個模塊。它的功能非常強大,可以操作URL中的所有部分。

  因此我們就可以改寫url,給用戶提供一個簡介大方的url,當用戶訪問時可以通過mod_rewrite模塊功能轉換為真正的資源路徑。通過mod_rewrite能實現的功能還有很多,例如隱藏真實地址、實現URL跳轉、域名跳轉、防盜鏈、限制訪問資源類型等等。

2、工作流程

  mod_rewrite模塊在運行時會使用兩個Hook程序。

  第一個是從URL到文件名轉換的Hook。當有訪問到達Apache服務器的時,服務器會確認相應主機(或虛擬主機),這時mod_rewrite模塊就開始工作,它將會先處理服務器全局中mod_rewrite模塊所提供的指令,然後根據用戶提供的指令進行改寫。

  第二個是修正URL的Hook。在此階段mod_rewrite模塊會處理非全局的設置。例如,目錄中的.htaccess文件中的設置。但是此時已經完成URL的翻譯(由URL轉換為文件名),因此是無法在次對目錄級別的URL進行改寫操作,但是moe_rewrite模塊會將已翻譯的URL再次轉換為URL的狀態,繼續進行目錄級別的URL改寫。(mod_rewrite模塊將會使用讀後請求階段的回叫函數重新開始一個請求的循環處理)

Rewirte模塊規則集的處理

  當mod_rewrite在這兩個API階段中開始執行時,它會讀取配置結構中配置好的 (或者是在服務啟動時建立的服務器級的,或者是在遍歷目錄采集到的目錄級的)規則集,然後,啟動URL重寫引擎來處理(帶有一個或多個條件的)規則集。無論是服務器級的還是目錄級的規則集,都是由同一個URL重寫引擎處理,只是最終結果處理不同而已。

  規則集中規則的順序是很重要的,因為重寫引擎是按一種特殊的順序處理的:逐個遍歷每個規則(RewriteRule指令),如果出現一個匹配條件的規則,則可能回頭遍歷已有的規則條件(RewriteCond指令)。由於歷史的原因,條件規則是前置的,所以控制流程略顯冗長,細節見圖-1。

技術分享圖片

  可見,URL首先與每個規則的Pattern匹配,如果匹配失敗,mod_rewrite將立即終止此規則的處理,繼而處理下一個規則。如果匹配成功,mod_rewrite將尋找相應的規則條件,如果一個條件都沒有,則簡單地用Substitution構造的新值來替換URL,然後繼續處理其他規則;但是如果條件存在,則開始一個內部循環按其列出的順序逐個處理。對規則條件的處理有所不同:URL並不與模式進行匹配,而是首先通過擴展變量、反向引用、查找映射表等步驟建立一個TestString字符串,然後用它來與CondPattern匹配。如果匹配失敗,則整個條件集和對應的規則失敗;如果匹配成功,則執行下一個規則直到所有條件執行完畢。如果所有條件得以匹配,則以Substitution替換URL,並且繼續處理。(本部分引用譯者:金步國)

網絡圖片:

              技術分享圖片

3、URL重寫指令

  最簡單的重寫指令可以簡單到讓你無法想象!

  只需要兩步就可以完成了。第一使用RewriteEngine開啟mod_rewrite模塊功能;第二通過RewriteRule定義URL重寫規則

  1)、URL重寫指令套路

1 ---------------------------------------------------------------
2 RewriteEngine on   #開啟mod_rewrite模塊功能
3 RewriteBase 路徑     #基準URL(使用alias設置別名則需使用這個)
4 RewriteCond TestString CondPattern [flags]      #重寫條件(可以多個)
5 RewriteRule Pattern Substitution [flags]          #重寫規則
6 ----------------------------------------------------------------
7 #4、5行可以可以多個
8 #按順序一個一個執行RewriteRule([flags不終止情況下])
9 ##以上是常用的指令,還有一些很少見的指令,需要的自己去查資料了解

  2)、RewriteRule Pattern Substitution [flags]

  1、pattern是作用於當前URL的perl兼容的正則表達式。當前URL是指該規則生效時刻的URL的值。它可能與被請求時的URL截然不同,因為之前可能被其他RewriteRule或者alias指令修改過。

  2、Substitution是當URL與Pattern匹配成功後。用來代替的字符串。

  • 可以對pattern反向引用$N(N=0~9),表示正則表達式中第N個括號中的內容
  • 對最後匹配的RewriteCond反向引用%N(N=0~9),表示最後匹配的RewriteCond第N對括號中的內容
  • 服務器變量%{VARNAME}
  • 映射函數調用${mapname:key|default} (通過RewriteMap指令定義映射輔助完成)

  3、[flags],標誌符,多個則用逗號隔開。

標誌符(摘抄於網上):

redirect|R [=code] (強制重定向 redirect)

  以 http://thishost[:thisport]/(使新的URL成為一個URI) 為前綴的Substitution可以強制性執行一個外部重定向。 如果code沒有指定,則產生一個HTTP響應代碼302(臨時性移動)。如果需要使用在300-400範圍內的其他響應代碼,只需在此指定這個數值即可, 另外,還可以使用下列符號名稱之一: temp (默認的), permanent, seeother. 用它可以把規範化的URL反饋給客戶端,如, 重寫“/~”為 “/u/”,或對/u/user加上斜杠,等等。

註意: 在使用這個標記時,必須確保該替換字段是一個有效的URL! 否則,它會指向一個無效的位置! 並且要記住,此標記本身只是對URL加上 http://thishost[:thisport]/的前綴,重寫操作仍然會繼續。通常,你會希望停止重寫操作而立即重定向,則還需要使用’L’標記.

forbidden|F (強制URL為被禁止的 forbidden)

  強制當前URL為被禁止的,即,立即反饋一個HTTP響應代碼403(被禁止的)。使用這個標記,可以鏈接若幹RewriteConds以有條件地阻塞某些URL。

gone|G(強制URL為已廢棄的 gone)

  強制當前URL為已廢棄的,即,立即反饋一個HTTP響應代碼410(已廢棄的)。使用這個標記,可以標明頁面已經被廢棄而不存在了.

proxy|P (強制為代理 proxy)

  此標記使替換成分被內部地強制為代理請求,並立即(即, 重寫規則處理立即中斷)把處理移交給代理模塊。你必須確保此替換串是一個有效的(比如常見的以 http://hostname開頭的)能夠為Apache代理模塊所處理的URI。使用這個標記,可以把某些遠程成分映射到本地服務器名稱空間, 從而增強了ProxyPass指令的功能。

註意: 要使用這個功能,代理模塊必須編譯在Apache服務器中。 如果你不能確定,可以檢查“httpd -l”的輸出中是否有mod_proxy.c。 如果有,則mod_rewrite可以使用這個功能;如果沒有,則必須啟用mod_proxy並重新編譯“httpd”程序。

last|L (最後一個規則 last)

  立即停止重寫操作,並不再應用其他重寫規則。 它對應於Perl中的last命令或C語言中的break命令。這個標記可以阻止當前已被重寫的URL為其後繼的規則所重寫。 舉例,使用它可以重寫根路徑的URL(’/’)為實際存在的URL, 比如, ‘/e/www/’.

next|N (重新執行 next round)

  重新執行重寫操作(從第一個規則重新開始). 這時再次進行處理的URL已經不是原始的URL了,而是經最後一個重寫規則處理的URL。它對應於Perl中的next命令或C語言中的continue命令。 此標記可以重新開始重寫操作,即, 立即回到循環的頭部。 但是要小心,不要制造死循環!

chain|C (與下一個規則相鏈接 chained)

  此標記使當前規則與下一個(其本身又可以與其後繼規則相鏈接的, 並可以如此反復的)規則相鏈接。 它產生這樣一個效果: 如果一個規則被匹配,通常會繼續處理其後繼規則, 即,這個標記不起作用;如果規則不能被匹配,則其後繼的鏈接的規則會被忽略。比如,在執行一個外部重定向時, 對一個目錄級規則集,你可能需要刪除“.www” (此處不應該出現“.www”的)。

type|T=MIME-type(強制MIME類型 type)

  強制目標文件的MIME類型為MIME-type。 比如,它可以用於模擬mod_alias中的ScriptAlias指令,以內部地強制被映射目錄中的所有文件的MIME類型為“application/x-httpd-cgi”。

nosubreq|NS (僅用於不對內部子請求進行處理 no internal sub-request)

  在當前請求是一個內部子請求時,此標記強制重寫引擎跳過該重寫規則。比如,在mod_include試圖搜索可能的目錄默認文件(index.xxx)時, Apache會內部地產生子請求。對子請求,它不一定有用的,而且如果整個規則集都起作用,它甚至可能會引發錯誤。所以,可以用這個標記來排除某些規則。

  根據你的需要遵循以下原則: 如果你使用了有CGI腳本的URL前綴,以強制它們由CGI腳本處理,而對子請求處理的出錯率(或者開銷)很高,在這種情況下,可以使用這個標記。

nocase|NC (忽略大小寫 no case)

  它使Pattern忽略大小寫,即, 在Pattern與當前URL匹配時,’A-Z’ 和’a-z’沒有區別。

qsappend|QSA (追加請求串 query string append)

  此標記強制重寫引擎在已有的替換串中追加一個請求串,而不是簡單的替換。如果需要通過重寫規則在請求串中增加信息,就可以使用這個標記。

noescape|NE (在輸出中不對URI作轉義 no URI escaping)

  此標記阻止mod_rewrite對重寫結果應用常規的URI轉義規則。 一般情況下,特殊字符(如’%’, ‘$’, ‘;’等)會被轉義為等值的十六進制編碼。 此標記可以阻止這樣的轉義,以允許百分號等符號出現在輸出中,如:

  RewriteRule /foo/(.*) /bar?arg=P1=$1 [R,NE] 可以使’/foo/zed’轉向到一個安全的請求’/bar?arg=P1=zed’.

passthrough|PT (移交給下一個處理器 pass through)

  此標記強制重寫引擎將內部結構request_rec中的uri字段設置為 filename字段的值,它只是一個小修改,使之能對來自其他URI到文件名翻譯器的 Alias,ScriptAlias, Redirect 等指令的輸出進行後續處理。舉一個能說明其含義的例子:如果要通過mod_rewrite的重寫引擎重寫/abc為/def,然後通過mod_alias使/def轉變為/ghi,可以這樣:

RewriteRule ^/abc(.*) /def$1 [PT]

Alias /def /ghi   如果省略了PT標記,雖然mod_rewrite運作正常, 即, 作為一個使用API的URI到文件名翻譯器,它可以重寫uri=/abc/…為filename=/def/…,但是,後續的mod_alias在試圖作URI到文件名的翻譯時,則會失效。

註意: 如果需要混合使用不同的包含URI到文件名翻譯器的模塊時, 就必須使用這個標記。。混合使用mod_alias和mod_rewrite就是個典型的例子。

For Apache hackers

  如果當前Apache API除了URI到文件名hook之外,還有一個文件名到文件名的hook, 就不需要這個標記了! 但是,如果沒有這樣一個hook,則此標記是唯一的解決方案。 Apache Group討論過這個問題,並在Apache 2.0 版本中會增加這樣一個hook。

skip|S=num (跳過後繼的規則 skip)

  此標記強制重寫引擎跳過當前匹配規則後繼的num個規則。 它可以實現一個偽if-then-else的構造: 最後一個規則是then從句,而被跳過的skip=N個規則是else從句. (它和’chain|C’標記是不同的!)

env|E=VAR:VAL (設置環境變量 environment variable)

  此標記使環境變量VAR的值為VAL, VAL可以包含可擴展的反向引用的正則表達式$N和%N。 此標記可以多次使用以設置多個變量。這些變量可以在其後許多情況下被間接引用,但通常是在XSSI (via ) or CGI (如 $ENV{’VAR’})中, 也可以在後繼的RewriteCond指令的pattern中通過%{ENV:VAR}作引用。使用它可以從URL中剝離並記住一些信息。

cookie|CO=NAME:VAL:domain[:lifetime[:path]] (設置cookie)

  它在客戶端瀏覽器上設置一個cookie。 cookie的名稱是NAME,其值是VAL。 domain字段是該cookie的域,比如’.apache.org’, 可選的lifetime是cookie生命期的分鐘數,可選的path是cookie的路徑。

  3)、RewriteCond TestString CondPattern [flags]

  Rewritecond指令定義一條規則條件。在一條rewriterule指令前面可能會有一條或者多條rewritecond指令,只有當自身模板匹配成功且這些條件也滿足時(即RewriteRule中的pattern匹配成功),規則條件才被應用於當前URL處理。

  1、TestString是一個純文本的字符串

  • 可以對pattern反向引用$N(N=0~9),緊跟在RewriteCond後面的RewriteRule正則表達式中第N個括號中的內容
  • 反向引用%N(N=0~9),表示RewriteCond中CondPattern中第N對括號中的內容
  • 服務器變量%{VARNAME}

  2、CondPattern是條件pattern,一個應用於當前實例TestString的正則表達式。即TestString與條件pattern條件進行匹配。如果匹配則RewriteCond的值為Rrue,反之為False

可以使用以下特殊變量(可使用‘!‘實現反轉):

‘>CondPattern’ (大於) 將condPattern當作一個普通字符串,將它和TestString進行比較,當TestString 的字符大於CondPattern為真。

‘=CondPattern’ (等於) 將condPattern當作一個普通字符串,將它和TestString進行比較,當TestString 與CondPattern完全相同時為真.如果CondPattern只是 “” (兩個引號緊挨在一起) 此時需TestString 為空字符串方為真。

‘-d’ (是否為目錄) 將testString當作一個目錄名,檢查它是否存在以及是否是一個目錄。

‘-f’ (是否是regular file) 將testString當作一個文件名,檢查它是否存在以及是否是一個regular文件。

‘-s’ (是否為長度不為0的regular文件) 將testString當作一個文件名,檢查它是否存在以及是否是一個長度大於0的regular文件。

‘-l’ (是否為symbolic link) 將testString當作一個文件名,檢查它是否存在以及是否是一個 symbolic link。

‘-F’ (通過subrequest來檢查某文件是否可訪問) 檢查TestString是否是一個合法的文件,而且通過服務器範圍內的當前設置的訪問控制進行訪問。這個檢查是通過一個內部subrequest完成的, 因此需要小心使用這個功能以降低服務器的性能。

‘-U’ (通過subrequest來檢查某個URL是否存在) 檢查TestString是否是一個合法的URL,而且通過服務器範圍內的當前設置的訪問控制進行訪問。這個檢查是通過一個內部subrequest完成的, 因此需要小心使用這個功能以降低服務器的性能。

   3、[flags]是第三個參數,多個標誌之間用逗號隔開

’nocase|NC’ (不區分大小寫)   在擴展後的TestString和CondPattern中,比較時不區分文本的大小寫。註意,這個標誌對文件系統和subrequest檢查沒有影響.

’ornext|OR’ (建立與下一個條件的或的關系)   默認的情況下,二個條件之間是AND的關系,用這個標誌將關系改為OR。

  4)、Rewrite時服務器變量(僅列出少數)

HTTP headers:HTTP_USER_AGENT, HTTP_REFERER, HTTP_COOKIE, HTTP_HOST, HTTP_ACCEPT

connection & request:REMOTE_ADDR, QUERY_STRING

server internals::DOCUMENT_ROOT, SERVER_PORT, SERVER_PROTOCOL

system stuff: TIME_YEAR, TIME_MON, TIME_DAY

  5)、簡單正則表達式規則

. 匹配任何單字符

[chars] 匹配字符串:chars

[^chars] 不匹配字符串:chars

text1|text2 可選擇的字符串:text1或text2

? 匹配0到1個字符

* 匹配0到多個字符

+ 匹配1到多個字符

^ 字符串開始標誌

$ 字符串結束標誌

\n 轉義符標誌

【註意】:一代Apache要求URL有斜杠而二代Apache卻不允許,因此使用 ^/?

4、例子解析

例1(簡單例子):

(在.htaccess裏進行規制重寫)

RewriteEngine ON 
RewriteRule  ^user/(w+)/?$user.php?id=$1

^:輸入的開頭 以user/開頭請求的地址

(w+):提取所有的字母,傳給$1

/?:可選斜杠

$:結束符

替換為:user.php?id=*

註意:有些apache(具體哪個版本忘啦)不兼容簡寫模式 w+ => [a-zA-Z_-]

例2(禁止IE和Opera瀏覽器訪問):

RewriteEngine on
RewriteCond %{HTTP_USER_AGENT} ^MSIE [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^Opera [NC]
RewriteRule ^.* - [F,L]       #‘-‘表示不替換URL

例3(不合法路徑返回首頁):

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]

例4(防盜鏈):

RewriteEngine On
RewriteCond %{HTTP_REFERER} !^http://(.+.)?mysite.com/ [NC]       #判斷請求的是否是自己的域名
RewriteCond %{HTTP_REFERER} !^$                     #{HTTP_REFERER}不為空
RewriteRule .*.(jpe?g|gif|bmp|png)$ /images/nohotlink.jpg [L]      #返回警告圖片

例5(改變訪問URL目錄名):

即隱藏真實的目錄名字

RewriteEngine On
RewriteRule ^/?old_dir/([a-z\.]+)$  new_dir/$1 [R=301,L]
#new_dir為真正目錄

例6(創建無文件後綴鏈接):

RewriteEngine On
RewriteCond %{REQUEST_FILENAME}.php -f #判斷該後綴文件是否存在
RewriteRule ^/?([a-zA-Z0-9]+)$ $1.php [L]
RewriteCond %{REQUEST_FILENAME}.html -f #判斷該後綴文件是否存在
RewriteRule ^/?([a-zA-Z0-9]+)$ $1.html [L]

例7(限制只能顯示圖片):

RewriteEngine on
RewriteCond %{REQUEST_FILENAME}  !^.*\.(gif|jpg|jpeg|png|swf)$
RewriteRule .*$ - [F,L]

例8(文件不存在重定向404):

RewriteEngine on
RewriteCond  %{REQUEST_FILENAME}  !f
RewriteCond  %{REQUEST_FILENAME}  !d
RewriteRule .? /404.php [L]

  https://cloud.tencent.com/developer/article/1192657

Apache URL重寫規則