<-
Apache > HTTP Server > 文件 > 版本 2.4 > Rewrite

透過 mod_rewrite 重新導向與重新映射

可用的語言:  en  |  fr 

這份文件補充了 mod_rewrite 參考文件。其中描述如何使用 mod_rewrite 重新導向與重新映射要求。這之中包含了很多 mod_rewrite 常見用法的範例,包括詳細說明每一個模組是如何運作的。

請注意這些範例有很多在您的伺服器設定中並不會運作,因此了解他們的原理比複製並貼上這些範例到您的設定中更為重要。
Support Apache!

另請參閱

top

從舊轉到新(內部)

說明

假設我們最近把 foo.html 的頁面重新命名為 bar.html,現在希望提供舊的網址作為後向相容性。但是,我們希望舊網址的使用者不察覺到該頁面已經重新命名,也就是說,我們不希望他們瀏覽器的網址列中的網址有所改變。

解決方案

我們會透過以下規則將舊的網址內部轉寫成新的網址

RewriteEngine  on
RewriteRule    "^/foo\.html$"  "/bar.html" [PT]
top

從舊重新寫到新(外部)

說明

假設我們近期把「foo.html」頁面重新命名為「bar.html」,而現在想提供舊網址以達到向下相容性。但這次我們希望舊網址的使用者能被提示到新的網址,也就是說瀏覽器的「位址」欄位也應該要修改。

解決方案

我們強制將 HTTP 重新導向到新的網址,這會導致瀏覽器以及使用者的檢視更動

RewriteEngine  on
RewriteRule    "^/foo\.html$"  "bar.html"  [R]
討論

在此範例中,與前述 內部 範例相反,我們可以單純使用 Redirect 指令。在較早的範例中使用 mod_rewrite 是為了向用戶端隱藏重新導向

Redirect "/foo.html" "/bar.html"
top

資源已移至其他伺服器

說明

如果資源已移至其他伺服器,而人們在更新書籤的期間,您可能希望網址能在舊伺服器上持續運作一段時間。

解決方案

您可以使用 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/"
top

從靜態到動態

說明

我們如何將靜態頁面「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]
top

檔案副檔名變更之向下相容性

說明

遷移 document.YYYYdocument.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 指令以指定您正在處理的目錄基礎。

top

正規主機名稱

說明
這項規則的目標是強制使用特定主機名稱,而優先於其他可能用於連線到相同網站的主機名稱。例如,如果您希望強制使用www.example.com而非example.com,您可以使用下列食譜的變異版本。
解決方案

解決這個問題的最佳方法根本無關乎 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檔中。

top

在多個目錄中搜尋網頁

說明

特定資源可能會存在於多個位置中的某個位置,而我們希望在要求時能在那些位置中尋找資源。也許我們最近重新整理了目錄結構,將內容分割到多個位置。

解決方案

以下規則集在兩個目錄中搜尋以尋找資源,如果在哪一個位置都找不到資源,則會嘗試僅僅從要求的位置提供服務。

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]
top

重新導向到依地理位置分布的伺服器

說明

我們有多個網站鏡像,並希望將使用者重新導向到位於其所在國家的網站鏡像。

解決方案

查看要求用戶端的主機名稱,我們會判斷其來源國家。如果我們無法透過其 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 檔中查找適當的鏡像主機。

top

瀏覽器相關內容

說明

我們希望根據瀏覽器或使用者代理要求提供不同的內容。

解決方案

我們必須根據 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]
top

規範網址

說明

在某個網頁伺服器上,資源有好幾個以上的網址。通常會有規範網址(實際使用且散布)以及許多捷徑、內部等網址。無論使用者在要求時提供哪個網址,最後都應該在瀏覽器網址列中看到規範網址。

解決方案

我們對所有非規範網址執行外部 HTTP 重新導向,以在瀏覽器的位置檢視和所有後續要求中修復這些網址。在下列範例規則集中,我們將把 /puppies/canines 取代為規範的 /dogs

RewriteRule   "^/(puppies|canines)/(.*)"    "/dogs/$2"  [R]
討論
這應該要使用 Redirect 或 RedirectMatch 指令來完成
RedirectMatch "^/(puppies|canines)/(.*)" "/dogs/$2"
top

已移除 DocumentRoot

說明

通常,網頁伺服器的 DocumentRoot 與網址「/」直接相關。但這項資料通常不是最高優先順序。例如,您可能希望造訪者第一次進入網站時開啟特定子目錄 /about/。這可以使用下一個規則集來執行

解決方案

我們將網址 / 重新導向至 /about/

RewriteEngine on
RewriteRule   "^/$"  "/about/"  [R]

請注意,也可以使用 RedirectMatch 指令來處理這項工作

RedirectMatch "^/$" "http://example.com/about/"

請另注意,這個範例僅重寫根目錄網址。亦即,它會重寫對 http://example.com/ 的要求,但不會重寫對 http://example.com/page.html 的要求。如果您實際上變更了文件根目錄,亦即所有內容實際上都在該子目錄中,則最好變更 DocumentRoot 指令,或將所有內容上移至上一層目錄,而非重寫網址。

top

備用資源

說明
您希望使用單一資源(例如特定的檔案,例如 index.php)來處理寄送到特定目錄的所有要求,除了應該傳送至現有資源(例如圖片或 CSS 檔案)的要求。
解決方案

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>
`區塊。

top

重寫查詢字串

說明
您想要從查詢字串擷取特定值,並替換它或將它納入 URL 的其他元件。
解決方案

本節的大部分解決方案都將使用相同的條件,而該條件會將已比對的值留在 %2 反向參考中。%1 是查詢字串開頭(直到感興趣的鍵為止),而 %3 其餘的部分。這個條件有點複雜,但為靈活性及避免替換中的雙重「&&」而設計。

  • 此解決方案會移除已比對的鍵值
    # Remove mykey=???
    RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$"
    RewriteRule "(.*)" "$1?%1%3"
  • 此解決方案會在 URL 替換中使用已擷取的值,並捨棄原始查詢的其餘部分,方法是附加「?」
    # 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]
  • 此解決方案示範前述的反向操作,將路徑元件(可能是 PATH_INFO)從 URL 複製到查詢字串。
    # The desired URL might be /products/kitchen-sink, and the script expects
    # /path?products=kitchen-sink.
    RewriteRule "^/?path/([^/]+)/([^/]+)" "/path?$1=$2" [PT]

可用的語言:  en  |  fr 

top

回饋

注意
這不是問答區。放在這裡的留言應針對改善文件或伺服器提出建議,若已套用或被視為無效/離題,我們的管理員可能會將其移除。針對如何管理 Apache HTTP Server 的相關問題應轉向我們的 IRC 頻道,位於 Libera.chat 上的 #httpd,或寄送至我們的郵寄清單