官網的說明在此:
http://httpd.apache.org/docs/current/mod/mod_rewrite.html
mod_rewrite對我來說算是常用的一個功能, 但是網路上很少找到完整的教學. 所以就只好自己看官網的文件邊啃邊測試了.
注意事項:
- 要使用 mod_rewrite 的虛擬主機或資料夾, 必須設定 Options FollowSymLinks, 否則無法運作.
- Pattern 比對的可能是 URL, 也可能是檔案路徑, 這要看我們在何處使用 RewriteRule. 差別在於 URL 開頭會有斜線, 路徑不會, 因此在寫 Pattern 時要注意.
- 有可能形成無限迴圈. 這會嚴重影響效能. 在新版 Apache 若改寫前後的檔案相同, 會自動停止.
設定紀錄檔:
1 2 3 4 5 |
<IfModule rewrite_module> # log level 0 ~ 9, set level to 0 to disable log RewriteLog /var/log/httpd/mod_rewrite.log RewriteLogLevel 9 </IfModule> |
在 httpd.conf 中加入以上設定, 就可以開啟 log 功能. log 等級從 1 到 9, 數字越大越詳細. 開發或除錯時可以很方便的瞭解實際的運作情形. 我們可以給每個虛擬主機設定不同的紀錄檔, 但無法給資料夾設定紀錄檔. 例如以下這段設定, 上面的可以執行, 下面的則會出現錯誤訊息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# this is working fine: <VirtualHost *:80> # ..... some setting for virtualhost <IfModule rewrite_module> # log level 0 ~ 9, set level to 0 to disable log RewriteLog /var/log/httpd/mod_rewrite.log RewriteLogLevel 9 </IfModule> </VirtualHost> # this will not work: <Directory "/path/to/web/root"> # ..... settings for directory <IfModule rewrite_module> # log level 0 ~ 9, set level to 0 to disable log RewriteLog /var/log/httpd/mod_rewrite.log RewriteLogLevel 9 </IfModule> </Directory> |
錯誤訊息會抱怨說 RewriteLog 這個設定不能使用在這邊
1 2 |
Syntax error on line xxx of /path/to/apache/httpd.conf: RewriteLog not allowed here |
基本語法:
1 |
RewriteRule Pattern Substitution [flags] |
這個設定就是 mod_rewrite 真正在做事情的地方了, Pattern 是用來比對我們想要改寫的原始內容, Substitution 則是我們要將其改寫成什麼樣子. 換句話說, 只有符合 Pattern 的情況下才會進行改寫的動作. flags 可以設定一些選項, 例如說不想區分大小寫, 就可以使用 [NC] 這個 flag.
在一般情況下, RewriteRule 是照順序一個一個執行下去的, 且前面一條改寫過的輸出會成為下一條的輸入, 所以安排好合理的順序會是很重要的關鍵. 下面的例子, 首先 about.htm 符合 Pattern, 變成 about1.php, 然後執行下一條規則, about1.php 又符合 Pattern, 所以最後會跑去 about2.php
1 2 3 4 5 6 7 8 9 |
# access: http://url.of.site/about.htm # rules: RewriteRule about.htm about1.php RewriteRule about1.php about2.php # result: http://url.of.site/about2.php |
兩種情境:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# Host context <VirtualHost *:80> # ..... some setting for virtualhost <IfModule rewrite_module> RewriteEngine On RewriteRule /about.htm /aboutme.php </IfModule> </VirtualHost> # per-directory context <Directory "/path/to/web/root"> # ..... settings for directory <IfModule rewrite_module> RewriteEngine On RewriteRule about.htm aboutme.php </IfModule> </Directory> |
當使用在 VirtualHost 標籤中, 或是不在任何標籤中, 將會以 Host 情境執行. 在這個情況下, 拿來與 Pattern 比對的是 URL, 包含開頭的斜線. 反之, 若我們是在 Directory 或是 htaccess 檔案中使用, 則會以 per-directory 情境執行, 這時會用檔案路徑去與 Pattern 比對. 例如我們分別用上面的設定來執行以下例子, 會發現在 log 中它是用不同的字串去與 Pattern 比對:
1 2 3 4 5 6 7 8 |
# access this url http://url.of.site/path/to/about.htm # Host context log applying pattern '/about.htm' to uri '/path/to/about.htm' # per-directory context log applying pattern 'about.htm' to uri 'path/to/about.htm' |
Pattern 的用法:
Pattern 的部份是使用正則表達式(Regular Expression), 在大部分常見的用途當中, 其實並不會用到太複雜的語法. 然而它畢竟是個威力強大的表示方法, 一有不小心就可能產生預期之外的結果, 例如:
1 2 3 4 5 6 7 8 |
# access: http://url.of.site/path/to/about.htm # rules: RewriteRule about.htm about.php # result: http://url.of.site/about.php |
注意這個例子前後 url 的變化, 路徑不見了, 只剩下檔名. 這是因為 Pattern 給的不夠精確, 造成我們不管存取任何路徑的 about.htm, 都會符合 Pattern. 而改寫時, Substitution 又不包含路徑, 所以路徑也就自然不見了.
單一入口頁面:
另外, 一個經典的使用方法, 是將所有的網頁請求交給單一頁面處理, 例如:
1 2 3 4 5 |
RewriteEngine on # 只要檔案或資料夾不存在, 導到 index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule !\.(js|ico|gif|jpg|png|css)$ /index.php |
但這邊用來判定檔案是否存在的方法需要注意, 若是在VirtualHost中使用, %{REQUEST_FILENAME}得到的是 /something.php 這樣的路徑, 於是rewrite會去檢查系統根目錄底下的something.php, 而不是網頁根目錄底下的檔案, 這會造成功能不正常, 可以說完全無法使用.
解決辦法, 就是加上<Location />, 並在其中設置rewrite的語法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<IfModule rewrite_module> RewriteEngine On # log for debug, set level to 0 to disable log RewriteLog /var/log/httpd/dtip-rewrite.log RewriteLogLevel 5 <Location /> RewriteEngine On # exists file RewriteCond %{REQUEST_FILENAME} -f [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^.*$ - [NC,L] # others all pass to index.php RewriteRule ^.*$ index.php [NC,L] </Location> </IfModule> |
強制使用 https :
如果想要將整個網站改成使用 https, 這是其中一種方法
1 2 3 4 5 6 7 |
<VirtualHost *:80> ServerName xxx.xxx.xxx RewriteEngine On RewriteCond %{HTTP_HOST} ^(.*)$ RewriteRule ^(.*)$ https://%1$1 [R=301,L] </VirtualHost> |