/ Work

[Rewrite note.] 重寫規則筆記

Apache 的 mod_rewrite 大家應該都很熟。然後,我以前也刻了超 60 行的重寫規則,不要問我為什麼要寫那麼多!因為,舊網站資料夾切的太細,重刻網站的時候,為了不要流失搜尋引擎過來的鍊結,只好這麼做了。

重寫規則,可以寫在 Virtual Host 設定中,也可以用在 .htaccess 裡面,差別是,沒差(喂)。但是記得這個東西要打開:

AllowOverride all

不打開的話 .htaccess 沒辦法運作喔(啾咪)。然後這行也要記得寫:

RewriteEngine On

不然重寫規則不會生效喔(啾咪)。這樣就差不多了(喂)。重寫規則的執行方式在 Apache mode_rewrite 中有很詳細的說明,如果有興趣了解的人 可以看看這張圖。這樣有理解了嗎?這個模組的運作方式大致上是這樣:

  1. 網址
  2. RewriteRule Pattern
  3. 取得該 Pattern
  4. 傳入 RewriteCond
  5. 執行 TestString
  6. 交給 CondPattern
  7. 執行 Rule 替代
  8. 重寫過後的網址 1
  9. 進入下一條 RewriteRule
  10. 重複 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]

請不要因為我公佈這個東西來攻擊我喔(啾咪)。