1. mod_rewrite
  2. 變數
  3. 小範例
  4. URL重寫條件規則
  5. URL重寫規則
  6. 註記
  7. 參考

 

URL重寫

主要用途為

  • 隱藏實體資源路徑與存取。如:防止隨意存取會影響檔案系統、資料庫之檔案
  • 簡化URL。例如將常用但冗長之網址縮短為易記之文字
  • 舊有URL相容。例如路徑變更、伺服器網址變更時,可將舊網址導向至新網址

最近剛好有這些需要就來玩玩

大部分還是玩Linux的 Apache HTTPD,再說Apache也有Windows版的

跨平台才是王道

Apache HTTPD博大精深,就算是mod_rewrite一、兩天還是只能窺見點皮毛而已

目前大概將基本款規則看過,配合「.htaccess」檔案建置Per-Directory重寫

本文列出基本常用的Per-directory URL重寫介紹,足夠應付大部分狀況

進階版本的例如Proxy、SSL還請參考官方文件[1] 章節

 


 

mod_rewrite

Apache HTTPD的 URL重寫主要由動態模組 mod_rewrite負責

mod_rewrite的執行分為 Per-server與 Per-directory兩種

Per-server設定將寫於http.conf以及 <VirtualHost> (註1) 區塊當中

Per-directory設定將寫於<Directory>區塊及特定目錄下 .htaccess檔案當中

URL重寫最基本款主要由一條條的重寫條件(Condition)與重寫規則(Rule)構成

重寫的運作則由一次次的迭代逐次改寫,直至兩次重寫結果相同為止

當然最大迭代次數是有限制的,以防迭代無法收斂而造長無窮迴圈

此時伺服器將會送出500 Internal Server Error [3]

mod_rewrite重寫流程大致如下

重寫規則可與Condition搭配,或者獨立存在

mod_rewrite flow  


 

變數

mod_rewrite提供下列幾種變數類型

可用於重寫之條件判斷,或展開於重寫之結果當中

    1. 伺服器變數 (Server Variable)

        由Apache伺服器提供之資訊,存取方式為 %{變數名稱}

  • HTTP標頭 (Header) 相關

    HTTP_USER_AGENT: 指示網頁瀏覽軟體之相關資訊
          同JavaScript的navigator.userAgent

    HTTP_REFERER: 指示網址之來源URL
          此欄位在點擊外部連結(<a>標籤)
          或頁面中索取其他資源(如圖片、JavaScript) 時
          將會附加於索取資源的HTTP Header中,指示索取資源之頁面URL

    HTTP_COOKIE: Cookie內容

    HTTP_HOST: 沒意外為伺服器之IP或網域名稱 (伺服器名稱)
  • 連線與要求 (Connection & Request) 相關
    以「http://example.com/~test/index.html?q1=2」為例

    REMOTE_ADDR: 客戶端IP位址

    REMOTE_HOST: 客戶端電腦名稱 (IP或網域名稱)

    REMOTE_PORT: 客戶端電腦連線開啟之連接埠

    REQUEST_METHOD: HTTP要求方法,如GET、POST等

    SCRIPT_FILENAME: 檔案系統資源路徑 (非URL而是經過解析過之檔案路徑位置)
          範例即 /home/test/index.html

    QUERY_STRING: 問號後之部分
          範例即 q1=2

  • 伺服器內部資訊

    DOCUMENT_ROOT: 伺服器文件存取根目錄之位置

    SERVER_ADMIN: 特權使用者帳號名稱,基本上為root

    SERVER_NAME

    SERVER_ADDR

    SERVER_PORT: 伺服器監聽之連接埠

    SERVER_PROTOCOL: 使用之通訊協定與版本,沒意外為 HTTP/1.1

  • 時間,如字面意思

    TIME_YEAR、TIME_MON、TIME_DAY
    TIME_HOUR、TIME_MIN、TIME_SEC
    TIME_WDAY、TIME

  • 特殊

    API_VERSION

    THE_REQUEST: HTTP要求開頭,如 GET /index.html HTTP/1.1,不含其他要求標頭值

    REQUEST_URI: 範例即 /~test/index.php  (不包含 Query String)

    REQUEST_FILENAME: 值同SCRIPT_FILENAME


    2. 環境變數

        可讀取任何環境變數值,存取方式為 %{ENV:變數名稱}

        自然也可以設定環境變數,見URL重寫規則章節

        例如:%{ENV:PATH}

        注意,每進入一新的pass,前一輪之所有環境變數會被加上「REDIRECT_」前綴字串

        因此每經過一輪,所有環境變數的名稱將會越變越長,官方文件並未正式記載此運作

    3. HTTP標頭值

        可讀取任何HTTP標頭欄位值,存取方式為 %{HTTP:標頭欄位名稱}

        如 %{HTTP:Host},即讀取HTTP標頭 Host欄位值

    4. Backreference

        以 $N 或 %N 作存取,1 ≦ N ≦ 9,主要用於字串比較並取得比較結果

        字串比較採用 PCRE (Peral-Compatible Regular Expression) [2]

        在重寫條件與重寫規則當中皆會使用到

        %N用於重寫條件,$N則用於重寫規則

        N則代表第N個 matching group,PCRE的matching group由 () 構成

        例如 ABC與 ^(A(B))(C)$ 作比較

        Group 1為 AB;Group 2為 B;Group3為 C

 


 

小範例

根據官方文件,URL要能夠運作

首先必須撰寫「RewriteEngine On」與「Options FollowSymLinks」

但我的case似乎可以不需要

且預設裝載的Apache也把許多指令都限制不可使用

本範例所要達成的目標為:

  1. 將沒有指定版本編號之URL前頭加上預設之版本編號目錄
  2. 將使用HTTP GET Method之URL,導向至Resource「目錄」下
  3. 將使用HTTP Post Method之URL,導向至Request「目錄」下
  4. 將 request「目錄下」特定資源,如,icon、javascript、css導向至專用目錄下
  5. 將其他URL,當作一般網頁,導向至版本目錄下之index.php
    並加上query string,method為request或resource
    並path為Resource或Request「目錄」後之部分
  6. 禁止直接存取index.php

本範例將越接近最後結果放置於較前之位置

理論上可在使用者直接輸入接近最後結果之URL時,加快處理速度

RewriteEngine ON
RewriteBase /~example/

# NOTICE: !! All environment variables of previous pass is prefixed with 'REDIRECT_'
RewriteCond %{ENV:REDIRECT_FINAL} =true
RewriteRule .* - [L]

# Prevent directly access to the result path
RewriteCond %{ENV:REDIRECT_FINAL} !=true
RewriteCond %{REQUEST_URI} ^/~crwp/v[0-9]+.[0-9]+/index.*
RewriteRule .* - [F]

# Final Rewrite
# Only the matching URL will be rewritten
# icon/image resource rewrite
RewriteRule ^(v[0-9]+.[0-9]+)/Resource/(icon|icons|img|image|images)/(.+\.(jpg|jpeg|gif|png))$ \
	$1/image/$3 [NC,L,E=FINAL:true]

# javascript resource rewrite
RewriteRule ^(v[0-9]+.[0-9]+)/Resource/(js|javascript|jslib)/(.+(\.js|\.map))$ \
	$1/js/$3 [NC,L,E=FINAL:true]

# CSS resource rewrite
RewriteRule ^(v[0-9]+.[0-9]+)/Resource/(css)/(.+\.css)$ $1/css/$3 [NC,L,E=FINAL:true]

# jsrender template
RewriteRule ^(v[0-9]+.[0-9]+)/Resource/(jtmpl|jhtml|jsrender)/(.+\.jhtml)$ \
	$1/jhtml/$3 [NC,L,E=FINAL:true]

# Others
RewriteRule ^(v[0-9]+.[0-9]+)/(Request|Resource)/(.*)$ $1/index.php?method=$2&path=$3 [NC,L,E=FINAL:true]

# Set POST method as Request
RewriteCond %{REQUEST_METHOD} =POST
RewriteCond %{REQUEST_URI} !^/~crwp/v[0-9]+.[0-9]+/request(/.*)?$ [NC]
RewriteRule ^(v[0-9]+.[0-9]+)/(.*)$ $1/Request/$2 [NC,L]

# Set GET method as Resource
RewriteCond %{REQUEST_METHOD} =GET
RewriteCond %{REQUEST_URI} !^/~crwp/(v[0-9]+.[0-9]+)/Resource(/.*)?$ [NC]
RewriteRule ^((v[0-9]+.[0-9]+)/(.*))$ $2/Resource/$3 [NC,L]

# add default version number
RewriteCond %{REQUEST_URI} !^/~crwp/v[0-9]+.[0-9]+(/.*)?$ [NC]
RewriteRule ^.*$ v2.0/$0 [L]

 


 

URL重寫條件規則

執行指令為 RewriteCond

依官方文件語法為 RewriteCond TestString Pattern [Flags]

TestString為必須測試之文字,基本上為使用變數

Pattern為比較之字樣,為PCRE語法

但亦包含下列特殊比較字元

    = : 比較TestString是否與Pattern完全相等,參照C語言 strcmp

    > : 比較TestString是否字串比較大於Pattern,參照C語言 strcmp

    < : 比較TestString是否字串比較小於Pattern,參照C語言 strcmp

    -d: 比較TestString是否為目錄

    -f : 比較TestString是否為一般檔案

    -s : 比較TestString是否為一般非空檔案

     -l : 比較TestString是否為Symbol Link (以Windows觀念即捷徑)

    -x : 比較TestString是否為有執行權限之檔案

若在Pattern開頭加上「!」符號,代表「非」、不符合條件

注意在一個pass中所有伺服器變數值是不變的,並不會因為重寫而改變

Flag為由逗點分隔之符號列表,可包含下列

    NC (No Case)

        字串比較不管大小寫

    OR (邏輯串接OR)

        以OR條件串接下列相鄰之RewriteCond,若不加則為 AND

範例:

    # REQUEST_URI不管大小寫者且為 /~test/x 開頭,或

    RewriteCond %{REQUEST_URI} ^/~test/x [OR, NC]

    # REQUEST_URI為 .jpg結尾 (「.」在PCRE中有特殊意義,「\」逃脫字元是必要的)

    RewriteCond %{REQUEST_URI} \.jpg$

 


 

URL重寫規則

指令為RewriteRule

依官方文件語法為 RewriteRule Pattern Substitution [Flag]

Pattern,重寫會先進行字串比較,若符合才進行內容替換及 Flag運行

有時可以簡化RewriteCond的撰寫

與Pattern比較的內容,在Per-Server的狀況下為URI部分

即主機網址後的部分,不包括Query String

在Per-Directory的狀況下,則為移除 Prefix (由RewriteBase指定) 之後的部分

Substition則為替換之內容,URL重寫會將原有內容完全替換為 Substition內容

最後再加上Prefix前綴字樣,成為新的URL

Substition可為

  • 檔案系統路徑,僅Per-Server可用

  • URI,替換的內容為相對於RewriteBase之路徑,若沒有指定
    則相對於 Document Root。若URI為實際存在之檔案,則
    Apache伺服器就不會再做進一步的URL解析。可含Query String

  • 絕對URL,即包含protocol、主機位址與連接埠之字串
    如 http://example:80/test.html,可含Query String

  • 「-」,即不進行改寫

Flag為逗點分隔之符號列表,包含下列

    C : chain
        即將下一相鄰之重寫規則結合為一群組
        由於RewriteCond符合時預設僅與一個RewriteRule結合
        使用C Flag則可將多個RewriteRule與 RewriteCond結合
        即 C語言的 if (condition) { rule1; rule2; ...}

    CO : cookie
        設定 cookie,設定語法為 CO={cookie名稱}:{cookie值}

    E : environment
        設定環境變數,設定語法為 E={環境變數名稱}:{環境變數值}
        若為 E=!{環境變數名稱},則刪除該環境變數

    F : forbidden
        mod_rewrite 將立即結束所有rewrite程序
        並回傳 HTTP 403 Forbidden錯誤狀態回應 [3]

    G : gone
        mod_rewrite 將立即結束所有 rewrite程序
        並回傳 HTTP 410 GONE錯誤狀態回應 [3]

    L : last rule
        官方文件記載結束重寫程序,忽略後續之RewriteRule
        此Flag為最易被誤解,事實上L Flag為結束目前的Pass (結束這回合)
        並進入下一輪,而非結束整個重寫程序

    N :next
        當RewriteRule Pattern相符時,重複執行此Rule
        即C語言 while (pattern match) substitue;

    NC: no case
        字串比較時忽略大小寫

    QSA:Query String Append
        將原有URL之Query String附加於重寫結果上。

    R : Redirect
        結束所有重寫程序,並回傳 HTTP重新導向狀態碼
        使用格式 R=3xx,預設為302。
        HTTP所定義之重新導向狀態碼,為3xx。[3]
        一般使用 301 (永久重新導向)或 302 (暫時重新導向)
        使用 R flag之後會發現,與不重新導向之運作不同
        網頁瀏覽器網址將變更為重寫之網址 (此將可能無法達成隱藏實體資源之目的)
        並且重新向Apache發送HTTP要求,並重新進行重寫程序

    S :skip
        使用格式為 S=N
        指跳過後續N個RewriteRule,類似C語言的goto
        

 


 

RewriteBase 

當網站不在Document Root之下時

例如個人家目錄 (預設為 /home/*/public_html) 或透過 Alias 轉換的目錄

當此狀況若不使用RewriteBase指令

一切的重寫結果都會當作在 Document Root底下之URL,而使URL存取錯誤

注意 RewriteBase之內容必定被當成目錄,而非字樣

因此若結尾未加上「/」,重寫後之結果將成為 {base}/rewrite

但若結尾加上「/」,RewriteRule Pattern所比較的字串便非「/」開頭

 

註記

    1. Name-based Virtual Host,依不同domain name提供不同document root位置服務

        在一台伺服器上營造有多台伺服器的感覺。

 

參考

   [1] Apache 2.2 mod_rewrite document

   [2] Apache 2.2 mod_rewrite regular expression

   [3] RFC 2616 HTTP - Status Line

文章標籤
創作者介紹
創作者 wylokgo101 的頭像
wylokgo101

豆棚瓜架雨如絲 - WYLOKGO101

wylokgo101 發表在 痞客邦 留言(0) 人氣()