Apache HTTP Server 版本 2.4
這份文件補充了 mod_rewrite
參考文件。其中描述如何使用 mod_rewrite
重新導向與重新映射要求。這之中包含了很多 mod_rewrite 常見用法的範例,包括詳細說明每一個模組是如何運作的。
DocumentRoot
假設我們最近把 foo.html
的頁面重新命名為 bar.html
,現在希望提供舊的網址作為後向相容性。但是,我們希望舊網址的使用者不察覺到該頁面已經重新命名,也就是說,我們不希望他們瀏覽器的網址列中的網址有所改變。
我們會透過以下規則將舊的網址內部轉寫成新的網址
RewriteEngine on RewriteRule "^/foo\.html$" "/bar.html" [PT]
假設我們近期把「foo.html」頁面重新命名為「bar.html」,而現在想提供舊網址以達到向下相容性。但這次我們希望舊網址的使用者能被提示到新的網址,也就是說瀏覽器的「位址」欄位也應該要修改。
我們強制將 HTTP 重新導向到新的網址,這會導致瀏覽器以及使用者的檢視更動
RewriteEngine on RewriteRule "^/foo\.html$" "bar.html" [R]
在此範例中,與前述 內部 範例相反,我們可以單純使用 Redirect 指令。在較早的範例中使用 mod_rewrite 是為了向用戶端隱藏重新導向
Redirect "/foo.html" "/bar.html"
如果資源已移至其他伺服器,而人們在更新書籤的期間,您可能希望網址能在舊伺服器上持續運作一段時間。
您可以使用 mod_rewrite
將這些網址重新導向到新的伺服器,但您也可以考慮使用 Redirect 或 RedirectMatch 指令。
#With mod_rewrite RewriteEngine on RewriteRule "^/docs/(.+)" "http://new.example.com/docs/$1" [R,L]
#With RedirectMatch RedirectMatch "^/docs/(.*)" "http://new.example.com/docs/$1"
#With Redirect Redirect "/docs/" "http://new.example.com/docs/"
我們如何將靜態頁面「foo.html」無縫轉換為動態變體「foo.cgi」,也就是說瀏覽器或使用者不會發現。
我們只重新撰寫 URL 至 CGI 腳本,然後強制處理常式為 cgi-script,以便以 CGI 程式執行。這樣可以透過將請求傳送至 /~quux/foo.html
在內部引發 /~quux/foo.cgi
。
RewriteEngine on RewriteBase "/~quux/" RewriteRule "^foo\.html$" "foo.cgi" [H=cgi-script]
遷移 document.YYYY
至 document.XXXX
之後,我們如何讓 URL 向下相容(仍然虛擬存在),例如在將大量 .html
檔轉換為 .php
之後?
我們將名稱重新撰寫為其基本檔名,然後測試新副檔名是否存在。如果存在,我們採用該名稱,否則我們重新撰寫 URL 至其原始狀態。
# backward compatibility ruleset for # rewriting document.html to document.php # when and only when document.php exists <Directory "/var/www/htdocs"> RewriteEngine on RewriteBase "/var/www/htdocs" RewriteCond "$1.php" -f RewriteCond "$1.html" !-f RewriteRule "^(.*).html$" "$1.php" </Directory>
此範例使用 mod_rewrite 的經常被忽略的功能,藉由利用規則集的執行順序。特別是,mod_rewrite 會在評估 RewriteCond 指令之前,評估 RewriteRule 的左端。因此,當評估 RewriteCond 指令時,$1 已經定義。這讓我們可以透過使用相同的檔案基本檔名,來測試原始(document.html
)和目標(document.php
)檔是否存在。
此規則集的設計適用於每一目錄的環境中(在 <目錄> 區塊中,或在 .htaccess 檔中),以便 -f
檢查會查看正確的目錄路徑。您可能需要設定 RewriteBase
指令以指定您正在處理的目錄基礎。
解決這個問題的最佳方法根本無關乎 mod_rewrite,而是使用Redirect
指令,將它放到虛擬主機的非標準主機名稱。
<VirtualHost *:80> ServerName undesired.example.com ServerAlias example.com notthis.example.com Redirect "/" "http://www.example.com/" </VirtualHost> <VirtualHost *:80> ServerName www.example.com </VirtualHost>
您也可以使用<If>
指令來達成這個目的
<If "%{HTTP_HOST} != 'www.example.com'"> Redirect "/" "http://www.example.com/" </If>
或者,例如,要重新導向網站的一部分到 HTTPS,您可以執行下列動作
<If "%{SERVER_PROTOCOL} != 'HTTPS'"> Redirect "/admin/" "https://www.example.com/admin/" </If>
如果基於種種原因,您仍然想要使用mod_rewrite
- 例如,您需要這項功能來處理更多組的 RewriteRules - 您可以在下列食譜當中選擇一個來使用。
針對在 80 埠以外執行之網站
RewriteCond "%{HTTP_HOST}" "!^www\.example\.com" [NC] RewriteCond "%{HTTP_HOST}" "!^$" RewriteCond "%{SERVER_PORT}" "!^80$" RewriteRule "^/?(.*)" "http://www.example.com:%{SERVER_PORT}/$1" [L,R,NE]
以及針對在 80 埠上執行的網站
RewriteCond "%{HTTP_HOST}" "!^www\.example\.com" [NC] RewriteCond "%{HTTP_HOST}" "!^$" RewriteRule "^/?(.*)" "http://www.example.com/$1" [L,R,NE]
如果您想要對所有網域名稱執行通用的動作 - 也即是,如果您想要將example.com重新導向到www.example.com,並針對example.com的所有可能值執行動作,您可以使用以下食譜
RewriteCond "%{HTTP_HOST}" "!^www\." [NC] RewriteCond "%{HTTP_HOST}" "!^$" RewriteRule "^/?(.*)" "http://www.%{HTTP_HOST}/$1" [L,R,NE]
這些規則集可以在您的主伺服器組態檔中執行,或是放到DocumentRoot
的.htaccess
檔中。
特定資源可能會存在於多個位置中的某個位置,而我們希望在要求時能在那些位置中尋找資源。也許我們最近重新整理了目錄結構,將內容分割到多個位置。
以下規則集在兩個目錄中搜尋以尋找資源,如果在哪一個位置都找不到資源,則會嘗試僅僅從要求的位置提供服務。
RewriteEngine on # first try to find it in dir1/... # ...and if found stop and be happy: RewriteCond "%{DOCUMENT_ROOT}/dir1/%{REQUEST_URI}" -f RewriteRule "^(.+)" "%{DOCUMENT_ROOT}/dir1/$1" [L] # second try to find it in dir2/... # ...and if found stop and be happy: RewriteCond "%{DOCUMENT_ROOT}/dir2/%{REQUEST_URI}" -f RewriteRule "^(.+)" "%{DOCUMENT_ROOT}/dir2/$1" [L] # else go on for other Alias or ScriptAlias directives, # etc. RewriteRule "^" "-" [PT]
我們有多個網站鏡像,並希望將使用者重新導向到位於其所在國家的網站鏡像。
查看要求用戶端的主機名稱,我們會判斷其來源國家。如果我們無法透過其 IP 位址進行查詢,我們會回到預設伺服器。
我們將使用RewriteMap
指令,來建置我們想要使用的伺服器清單。
HostnameLookups on RewriteEngine on RewriteMap multiplex "txt:/path/to/map.mirrors" RewriteCond "%{REMOTE_HOST}" "([a-z]+)$" [NC] RewriteRule "^/(.*)$" "${multiplex:%1|http://www.example.com/}$1" [R,L]
## map.mirrors -- 多工地圖
de http://www.example.de/
uk http://www.example.uk/
com http://www.example.com/
##EOF##
HostNameLookups
被設定為on
,這可能會嚴重影響效能。指令 RewriteCond
擷取要求端客戶端主機名的最後一部分,即國碼,下一個 RewriteRule 使用該值在 map 檔中查找適當的鏡像主機。
我們希望根據瀏覽器或使用者代理要求提供不同的內容。
我們必須根據 HTTP 標頭「使用者代理」決定要提供哪些內容。下一個設定檔執行下列動作:如果 HTTP 標頭「使用者代理」包含「Mozilla/3」,則頁面 foo.html
會重寫為 foo.NS.html
且停止重寫。如果瀏覽器版本為 1 或 2 的「Lynx」或「Mozilla」,則網址會變成 foo.20.html
。所有其他瀏覽器會收到 foo.32.html
。此動作可使用下列規則集來執行
RewriteCond "%{HTTP_USER_AGENT}" "^Mozilla/3.*" RewriteRule "^foo\.html$" "foo.NS.html" [L] RewriteCond "%{HTTP_USER_AGENT}" "^Lynx/" [OR] RewriteCond "%{HTTP_USER_AGENT}" "^Mozilla/[12]" RewriteRule "^foo\.html$" "foo.20.html" [L] RewriteRule "^foo\.html$" "foo.32.html" [L]
在某個網頁伺服器上,資源有好幾個以上的網址。通常會有規範網址(實際使用且散布)以及許多捷徑、內部等網址。無論使用者在要求時提供哪個網址,最後都應該在瀏覽器網址列中看到規範網址。
我們對所有非規範網址執行外部 HTTP 重新導向,以在瀏覽器的位置檢視和所有後續要求中修復這些網址。在下列範例規則集中,我們將把 /puppies
和 /canines
取代為規範的 /dogs
。
RewriteRule "^/(puppies|canines)/(.*)" "/dogs/$2" [R]
RedirectMatch "^/(puppies|canines)/(.*)" "/dogs/$2"
DocumentRoot
通常,網頁伺服器的 DocumentRoot
與網址「/
」直接相關。但這項資料通常不是最高優先順序。例如,您可能希望造訪者第一次進入網站時開啟特定子目錄 /about/
。這可以使用下一個規則集來執行
我們將網址 /
重新導向至 /about/
RewriteEngine on RewriteRule "^/$" "/about/" [R]
請注意,也可以使用 RedirectMatch
指令來處理這項工作
RedirectMatch "^/$" "http://example.com/about/"
請另注意,這個範例僅重寫根目錄網址。亦即,它會重寫對 http://example.com/
的要求,但不會重寫對 http://example.com/page.html
的要求。如果您實際上變更了文件根目錄,亦即所有內容實際上都在該子目錄中,則最好變更 DocumentRoot
指令,或將所有內容上移至上一層目錄,而非重寫網址。
2.2.16 版開始就應該使用 FallbackResource
指令處理這項工作
<Directory "/var/www/my_blog"> FallbackResource "index.php" </Directory>
但是,在 Apache 的早期版本中,或者如果您的需求比這更複雜,您可以使用下列重寫指令的變體來完成相同的事情
<Directory "/var/www/my_blog"> RewriteBase "/my_blog" RewriteCond "/var/www/my_blog/%{REQUEST_FILENAME}" !-f RewriteCond "/var/www/my_blog/%{REQUEST_FILENAME}" !-d RewriteRule "^" "index.php" [PT] </Directory>
另一方面,如果您希望將請求的 URI 作為查詢字串引數傳遞至 index.php,您可以用下列指令取代該 RewriteRule
RewriteRule "(.*)" "index.php?$1" [PT,QSA]
請注意這些規則集可用於`
.htaccess`檔案,以及`
<Directory>`區塊。
本節的大部分解決方案都將使用相同的條件,而該條件會將已比對的值留在 %2 反向參考中。%1 是查詢字串開頭(直到感興趣的鍵為止),而 %3 其餘的部分。這個條件有點複雜,但為靈活性及避免替換中的雙重「&&」而設計。
# Remove mykey=??? RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$" RewriteRule "(.*)" "$1?%1%3"
# Copy from query string to PATH_INFO RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$" RewriteRule "(.*)" "$1/products/%2/?" [PT]
# Capture the value of mykey in the query string RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$" RewriteCond "%2" !=not-so-secret-value RewriteRule "(.*)" - [F]
# The desired URL might be /products/kitchen-sink, and the script expects # /path?products=kitchen-sink. RewriteRule "^/?path/([^/]+)/([^/]+)" "/path?$1=$2" [PT]