Apache 的 mod_rewrite
大家應該都很熟。然後,我以前也刻了超 60 行的重寫規則,不要問我為什麼要寫那麼多!因為,舊網站資料夾切的太細,重刻網站的時候,為了不要流失搜尋引擎過來的鍊結,只好這麼做了。
重寫規則,可以寫在 Virtual Host 設定中,也可以用在 .htaccess 裡面,差別是,沒差(喂)。但是記得這個東西要打開:
AllowOverride all
不打開的話 .htaccess 沒辦法運作喔(啾咪)。然後這行也要記得寫:
RewriteEngine On
不然重寫規則不會生效喔(啾咪)。這樣就差不多了(喂)。重寫規則的執行方式在 Apache mode_rewrite 中有很詳細的說明,如果有興趣了解的人 可以看看這張圖。這樣有理解了嗎?這個模組的運作方式大致上是這樣:
- 網址
- RewriteRule Pattern
- 取得該 Pattern
- 傳入 RewriteCond
- 執行 TestString
- 交給 CondPattern
- 執行 Rule 替代
- 重寫過後的網址 1
- 進入下一條 RewriteRule
- 重複 2~9 的步驟
這裡有兩個東西,第一個是 RewriteCond,第二個是 RewriteRule(暫且不談 RewriteMap)。這兩個是最常見也最常用的東西,大致上可以這麼說:
- RewriteCond:定義一條重寫測試規則,且測試。
- RewriteRule:取得重寫字串,並套入重寫規則。
- RewriteLog:可以紀錄重寫規則(看名字應該就知道了)。
- RewriteLogLevel:紀錄重寫規則的複雜程度,最小 0 最大 9,顧名思義,9 就是什麼都有。
- RewriteBase:重寫規則以哪一個網址層級為基準。
- RewriteOptions:最常用是 MaxRedirects=number,限制重寫次數,避免改到服務器掛掉(笑)。
首先是 RewrireRule 的 Pattern:
RewriteRule ^(.*)$ ./index.php/$1 [L,QSA]
RewriteRule Pattern Substitution
基本上就是正規表示式,但是有一些但書!
- 可以使用
!
來當作是否定條件(否定批配),但是,你的記憶群組(使用( )
正規表示)會無法使用。因為你用了!
(否定批配),所以所記憶的群組因為是否定的關係,所以無法在最後 Rule 替代(Substitution)時使用,也就是無法使用$N
這種參考。 - 官方參考:http://perldoc.perl.org/perlre.html
- 注意 / 在這裡面的用法,記得要跳脫(escape),畢竟 / 是切開網址(資料夾)用的。
- 可以單純用減號(
-
),表示我沒有替代(Substitution)結果,直接將比對結果輸出。
接著是 RewriteCond 的 TestString:
RewriteCond %{REMOTE_FILENAME} -f
RewriteCond TestString CondPattern
在條件中的測試字串,有很多可用(RewriteMap 跳過):
- RewriteRule 參考,就是
$N
。 - RewriteCond 參考,就是
%N
,這要連續才有用,也就是上一條的。 - RewriteMap,跳過先(喂)。
- Server Variables,伺服器變數,這是最常見的。
- 但書?當然有囉,詳見官網說明
再來是 RewriteCond 的 CondPattern,這裡一樣可以用 " ! "(否定批配),但是記得,否定批配的記憶群組不可參考。大致上有:
<CondPattern
:按照單字順序比對(升冪比對)。>CondPattern
:按照單字順序比對(降冪比對)。=CondPattern
:按照單字順序比對(完全相同)。-d
:比對 CondPattern 為路徑(資料夾)。-f
:比對 CondPattern 為檔案。-s
:比對 CondPattern 為檔案,但是檔案大小要大於 0。-F
:比對 CondPattern 為檔案,但是會檢查伺服器配置,確認該檔案有存取權限。-U
:比對 CondPattern 為路徑,但是會檢查伺服器配置,確認該路徑有存取權限。
CondPattern 後面可以追加兩組標籤:
[OR]
:請把他當作是條件式的 AND 來看。[NC]
:就是 No Case 的意思,不管大小寫比對。
最後會回到 RewriteRule 的 Substitution,這裡就是最後輸出的網址樣式。他也可以是減號(-
),直接輸出比對結果,不做任何處理。最後可以附加一組標籤,這組標籤是用來做最後判別用。這組標籤請謹慎,不然小心掉入無限重寫轉址地獄喔(啾咪)。
C
:這是很有趣的標籤,倘若你的測試條件不符合,那往後的條件就不會被發生。意思就是,如果第一條寫了[C]
,那倘若第一條符合,就不會發生任何事情繼續比對下去。但是倘若第一條不符合,那麼接下來的第二條就會跳過不執行比對了。CO=NAME:VAL:domain[:lifetime[:path]]
: 設定一組 cookie 用的。E=VAR:VAL
:設定系統環境變數用的。F
:吐回 403,就是否嗶等啦!G
:吐回 401,就是這網址已經不存在了。L
:最後一支舞(喂)最後一個條件,表示之前的測試到了這裡倘若符合就會結束,不會往下做。N
:下一回合(噹噹噹)。讓測試重頭開始,請小心不要掉入無窮迴圈。NC
:就是 No Case,僅此而已。NE
:對所測試的網址輸出不做跳脫處理(escape)。NS
:不使用內部請求處理。所謂內部請求,大抵上是指這個網址不需要由伺服器端的執行程式處理(像是.php
結尾,是由伺服器 PHP Engine 處理)。所以,像是 Apache 所擁有的 server-status 這種,我就可以使用NS
讓他不會透過 PHP Engine 來處理這類輸出(這是我對官方的解釋的理解,如果覺得不對,請參考官方的說明P
:強制使用 Proxy,這個需要mod_proxy
這個套件喔!PT
:取代部份的請求網址,可以搭配mod_alias
使用。QSA
:傳回網址後面的QUERY STRING
。就是?a=xxx&b=xxx&c=xxx
這一段。R[=code]
:強制轉址。後面可以加上 HTTP Status Code,常用的大概就是 302 吧,告訴人家說你已經搬家了(笑)。S=num
:跳過往後 num 條的規則。T
:強制使用某個MIME-Type
來處理目前的轉址。
ps, 這邊舉一個 PT
的簡單範例,如果指向 /img
的請求網址,實際上是指向 /icons
底下的檔案,那就可以這樣寫:
RewriteRule /img/(.+)\.jpg /icons/$1.jpg [PT]
然後呢,就直接拿 TextCube 的 .htaccess
來當個簡單的例子吧!
# 打開重寫引擎
RewriteEngine On
# 設定基準目錄,從根目錄開始比對
RewriteBase /
# 比對網址,倘若符合 thumbnail,則轉址到 cache 底下,至此結束。
RewriteRule ^(thumbnail)/([0-9]+/.+)$ cache/$1/$2 [L]
# 重新比對網址,倘若結尾是 mp3, mov, ogg, mp4, avi, wmv,不做任何替代,直接 404,至此結束。
RewriteRule (.[^\.]).(mp3|mov|ogg|mp4|avi|wmv)$ - [NC,F,L]
# 設定比對條件,若請求的網址是檔案。
RewriteCond %{REQUEST_FILENAME} -f
# 重新比對網址,倘若是 cache,但是非 sitemap 底下,結尾是 .cache, .xml, .txt, .log,不做任何替代,直接 404,至此結束。
RewriteRule ^(cache)+/+([^sitemap/])(.+[^/]).(cache|xml|txt|log)$ - [NC,F,L]
# 重新比對網址,倘若請求網址是資料夾。
RewriteCond %{REQUEST_FILENAME} -d
# 在結尾加上 /,至此結束。
RewriteRule ^(.+[^/])$ $1/ [L]
# 重新比對網址,倘若請求網址不是檔案。
RewriteCond %{REQUEST_FILENAME} !-f
# 將請求網址轉給 rewrite.php 這支檔案,至此結束,並連同 Query String 一併傳入。
RewriteRule ^(.*)$ rewrite.php [L,QSA]
請不要因為我公佈這個東西來攻擊我喔(啾咪)。