
這是一個規範問題關於 Apache 的 mod_rewrite。
更改請求 URL 或將使用者重新導向到與最初請求的 URL 不同的 URL 是使用 mod_rewrite 完成的。這包括以下內容:
- 將 HTTP 變更為 HTTPS(或相反)
- 將對不再存在的頁面的請求更改為新的替換。
- 修改 URL 格式(例如 ?id=3433 修改為 /id/3433 )
- 基於瀏覽器、基於引薦來源網址、基於月亮和太陽下任何可能的情況呈現不同的頁面。
- 任何你想用 URL 搞亂的東西
關於 Mod_Rewrite 規則您想知道但不敢問的一切!
我怎樣才能成為編寫 mod_rewrite 規則的專家?
- mod_rewrite規則的基本格式和架構是什麼?
- 我需要牢牢掌握正規表示式的哪一種形式/風格?
- 編寫重寫規則時最常見的錯誤/陷阱是什麼?
- 測試和驗證 mod_rewrite 規則的好方法是什麼?
- 我應該注意 mod_rewrite 規則對 SEO 或效能的影響嗎?
- 是否存在 mod_rewrite 看似適合該工作但實際上並非如此的常見情況?
- 有哪些常見的例子?
測試你的規則的地方
這htaccess測試儀網站是嘗試並測試規則的好地方。它甚至顯示偵錯輸出,以便您可以看到哪些匹配,哪些不匹配。
答案1
mod_rewrite 語法順序
mod_rewrite 有一些影響處理的特定排序規則。在完成任何操作之前,RewriteEngine On
需要給出指令,因為這會開啟 mod_rewrite 處理。這應該在任何其他重寫指令之前。
RewriteCond
前面的RewriteRule
使得該 ONE 規則受條件約束。任何後續 RewriteRules 將被處理,就好像它們不受條件約束一樣。
RewriteEngine On
RewriteCond %{HTTP_REFERER} ^https?://serverfault\.com(/|$)
RewriteRule $/blog/(.*)\.html $/blog/$1.sf.html
在這個簡單的範例中,如果 HTTP 引用來自 serverfault.com,則將部落格請求重新導向到特殊的 serverfault 頁面(我們就是那麼特殊)。但是,如果上面的區塊有一個額外的 RewriteRule 行:
RewriteEngine On
RewriteCond %{HTTP_REFERER} ^https?://serverfault\.com(/|$)
RewriteRule $/blog/(.*)\.html $/blog/$1.sf.html
RewriteRule $/blog/(.*)\.jpg $/blog/$1.sf.jpg
所有 .jpg 檔案都將轉到特殊的伺服器故障頁面,而不僅僅是那些帶有指示其來自此處的引薦來源網址的頁面。這顯然不是這些規則的編寫本意。可以使用多個 RewriteCond 規則來完成:
RewriteEngine On
RewriteCond %{HTTP_REFERER} ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.html /blog/$1.sf.html
RewriteCond %{HTTP_REFERER} ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.jpg /blog/$1.sf.jpg
但可能應該使用一些更棘手的替換語法來完成。
RewriteEngine On
RewriteCond %{HTTP_REFERER} ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.(html|jpg) /blog/$1.sf.$2
更複雜的 RewriteRule 包含用於處理的條件。最後一個括號(html|jpg)
告訴 RewriteRule 符合html
或jpg
,並在重寫的字串中將匹配的字串表示為 $2 。這在邏輯上與前一個區塊相同,有兩個 RewriteCond/RewriteRule 對,它只是在兩行而不是四行上執行。
多個 RewriteCond 行可以隱式進行 AND 運算,也可以明確地進行 OR 運算。處理來自 ServerFault 和超級使用者的引薦來源網址(明確 OR):
RewriteEngine On
RewriteCond %{HTTP_REFERER} ^https?://serverfault\.com(/|$) [OR]
RewriteCond %{HTTP_REFERER} ^https?://superuser\.com(/|$)
RewriteRule ^/blog/(.*)\.(html|jpg) /blog/$1.sf.$2
若要使用 Chrome 瀏覽器提供 ServerFault 引用的頁面(隱含 AND):
RewriteEngine On
RewriteCond %{HTTP_REFERER} ^https?://serverfault\.com(/|$)
RewriteCond %{HTTP_USER_AGENT} ^Mozilla.*Chrome.*$
RewriteRule ^/blog/(.*)\.(html|jpg) /blog/$1.sf.$2
RewriteBase
也是特定於順序的,因為它指定以下RewriteRule
指令如何處理它們的處理。它在 .htaccess 檔案中非常有用。如果使用,它應該是 .htaccess 檔案中“RewriteEngine on”下的第一個指令。舉個例子:
RewriteEngine On
RewriteBase /blog
RewriteCond %{HTTP_REFERER} ^https?://serverfault\.com(/|$)
RewriteRule ^(.*)\.(html|jpg) $1.sf.$2
這告訴 mod_rewrite 它目前正在處理的這個特定 URL 是透過以下方式到達的http://example.com/blog/而不是物理目錄路徑 (/home/$Username/public_html/blog) 並相應地對待它。因此,RewriteRule
認為字串開始位於 URL 中的“/blog”之後。這是用兩種不同方式寫的同一件事。一個帶有 RewriteBase,另一個沒有:
RewriteEngine On
##Example 1: No RewriteBase##
RewriteCond %{HTTP_REFERER} ^https?://serverfault\.com(/|$)
RewriteRule /home/assdr/public_html/blog/(.*)\.(html|jpg) $1.sf.$2
##Example 2: With RewriteBase##
RewriteBase /blog
RewriteCond %{HTTP_REFERER} ^https?://serverfault\.com(/|$)
RewriteRule ^(.*)\.(html|jpg) $1.sf.$2
正如您所看到的,RewriteBase
允許重寫規則以利用網路-地點內容路徑而不是網路 -伺服器,這可以使編輯此類文件的人更容易理解它們。此外,它們還可以使指令更短,從而具有美觀性。
RewriteRule匹配語法
RewriteRule 本身有一個用於匹配字串的複雜語法。我將在另一節中介紹標誌(例如 [PT])。因為系統管理員透過範例學習的次數比透過閱讀手冊頁我將舉例並解釋它們的作用。
RewriteRule ^/blog/(.*)$ /newblog/$1
此.*
構造與任何單一字元 ( .
) 相符零次或多次 ( *
)。將其括在括號中告訴它提供與 $1 變數相符的字串。
RewriteRule ^/blog/.*/(.*)$ /newblog/$1
在這種情況下,第一個 .* 未括在括號中,因此不會提供給重寫的字串。此規則刪除新部落格網站上的目錄層級。 (/blog/2009/sample.html 變成 /newblog/sample.html)。
RewriteRule ^/blog/(2008|2009)/(.*)$ /newblog/$2
在這種情況下,第一個括號表達式設定一個符合組。這變成了 $1,它是不需要的,因此不會在重寫的字串中使用。
RewriteRule ^/blog/(2008|2009)/(.*)$ /newblog/$1/$2
在本例中,我們在重寫的字串中使用 $1。
RewriteRule ^/blog/(20[0-9][0-9])/(.*)$ /newblog/$1/$2
該規則使用特殊的括號語法來指定一個字符範圍。 [0-9] 符合數字 0 到 9。
RewriteRule ^/blog/(20[0-9]{2})/(.*)$ /newblog/$1/$2
這與前面的規則執行相同的操作,但 {2} 部分告訴它要符合前面的字元(在本例中為括號表達式)兩次。
RewriteRule ^/blog/([0-9]{4})/([a-z]*)\.html /newblog/$1/$2.shtml
這種情況將匹配第二個匹配表達式中的任何小寫字母,並儘可能多地匹配字元。這個\.
構造告訴它將句點視為實際句點,而不是前面範例中的特殊字元。但是,如果檔案名稱中包含破折號,它就會中斷。
RewriteRule ^/blog/([0-9]{4})/([-a-z]*)\.html /newblog/$1/$2.shtml
這會捕獲帶有破折號的檔案名稱。然而,由於-
括號表達式中的特殊字符,它必須是第一的表達式中的字元。
RewriteRule ^/blog/([0-9]{4})/([-0-9a-zA-Z]*)\.html /newblog/$1/$2.shtml
-
此版本可擷取檔案名稱中包含字母、數字或字元的任何檔案名稱。這是在括號表達式中指定多個字元集的方法。
重寫規則標誌
RewriteRule ^/blog/([0-9]{4})/([-a-z]*).\html /newblog/$1/$2.shtml [L]
標誌是[L]
上述表達式末尾的 。可以使用多個標誌,用逗號分隔。連結的文檔描述了每一個,但它們仍然在這裡:
L= 最後。一旦匹配,就停止處理 RewriteRules。訂單算數!
C= 鏈。繼續處理下一個RewriteRule。如果這條規則不匹配,則不會執行下一條規則。稍後會詳細介紹這一點。
乙= 設定環境變數。 Apache 有各種可能影響 Web 伺服器行為的環境變數。
F= 禁止。如果此規則匹配,則傳回 403-Forbidden 錯誤。
G= 走了。如果此規則匹配,則傳回 410-Gone 錯誤。
H= 處理程序。強制處理請求,就好像它是指定的 MIME 類型一樣。
氮= 下一步。強制規則重新開始並重新配對。當心!可能會導致循環。
數控= 沒有案例。允許jpg
匹配 jpg 和 JPG。
東北= 無法逃脫。防止將特殊字元(. ? # & 等)重寫為其等效的十六進位代碼。
NS= 無子請求。如果您使用伺服器端包含,這將阻止與包含的檔案相符。
磷= 代理。強制規則由 mod_proxy 處理。透明地提供來自其他伺服器的內容,因為您的 Web 伺服器會取得它並重新提供它。這是一個危險的標誌,因為寫得不好的標誌會將您的網頁伺服器變成開放代理,這很糟糕。
PT= 通過。在 RewriteRule 匹配中考慮 Alias 語句。
品質安全協會= QS附加。當原始字串包含查詢(http://example.com/thing?asp=foo) 將原始查詢字串附加到重寫的字串中。正常情況下會被丟棄。對於動態內容很重要。
右= 重定向。提供到指定 URL 的 HTTP 重新導向。也可以提供準確的重定向程式碼 [R=303]。與 非常相似RedirectMatch
,速度更快,應盡可能使用。
S= 跳過。跳過這條規則。
時間= 類型。指定傳回內容的mime-type。與指令非常相似AddType
。
你知道我怎麼說這RewriteCond
適用於一項且僅一項規則嗎?好吧,你可以透過連結來解決這個問題。
RewriteEngine On
RewriteCond %{HTTP_REFERER} ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.html /blog/$1.sf.html [C]
RewriteRule ^/blog/(.*)\.jpg /blog/$1.sf.jpg
因為第一個 RewriteRule 有 Chain 標誌,所以第二個重寫規則會在第一個重寫規則執行時執行,也就是當一個 RewriteCond 規則符合時。如果 Apache 正規表示式讓你的大腦受傷的話,這會很方便。然而,從最佳化的角度來看,我在第一部分中指出的一體化方法更快。
RewriteRule ^/blog/([0-9]{4})/([-0-9a-zA-Z]*)\.html /newblog/$1/$2.shtml
這可以透過標誌變得更簡單:
RewriteRule ^/blog/([0-9]{4})/([-0-9a-z]*)\.html /newblog/$1/$2.shtml [NC]
此外,有些標誌也適用於 RewriteCond。值得注意的是,NoCase。
RewriteCond %{HTTP_REFERER} ^https?://serverfault\.com(/|$) [NC]
將匹配“ServerFault.com”
答案2
mod_rewrite規則的基本格式和架構是什麼?
我會尊重 sysadmin1138 在這些問題上的出色回答。
我需要牢牢掌握正規表示式的哪一種形式/風格?
除了 sysadmin1138 概述的語法順序、語法匹配/正規表示式和 RewriteRule 標誌之外,我認為值得一提的是 mod_rewrite 公開了基於 HTTP 請求標頭和 Apache 配置的 Apache 環境變數。
我會推薦AskApache 的 mod_rewrite 調試教程查看 mod_rewrite 可用的變數的完整清單。
編寫重寫規則時最常見的錯誤/陷阱是什麼?
RewriteRule 的大多數問題源於對 PCRE 語法的誤解/未能正確轉義特殊字元或缺乏對用於匹配的變數內容的了解。
典型問題及建議的故障排除:
- 500內部伺服器錯誤-刪除 Windows 回車控件在設定檔中(如果存在),請確保 mod_rewrite 已啟用(將指令包裝在
IfModule
有條件避免這種情況),檢查指令語法,註解掉指令,直到發現問題 - 重定向循環- 利用 RewriteLog 和 RewriteLogLevel,註解掉指令直到發現問題
測試和驗證 mod_rewrite 規則的好方法是什麼?
首先,請查看您計劃匹配的環境變數的內容 - 如果您安裝了 PHP,則只需將以下區塊新增至您的應用程式:
<?php
var_dump($_SERVER);
?>
....然後編寫您的規則(最好用於在開發伺服器上進行測試)並記下 Apache 中任何不一致的匹配或活動錯誤日誌文件。
更複雜的規則,請使用 mod_rewriteRewriteLog
將活動記錄到檔案並設定的指令RewriteLogLevel 3
我應該注意 mod_rewrite 規則對 SEO 或效能的影響嗎?
AllowOverride all
影響伺服器效能,因為 Apache 必須檢查.htaccess
檔案並解析每個請求的指令 - 如果可能,請將所有指令保留在網站的 VirtualHost 設定中,或.htaccess
僅對需要它們的目錄啟用覆蓋。
谷歌的網站管理員指南明確聲明:「不要欺騙用戶或向搜尋引擎呈現與向用戶顯示的內容不同的內容,這通常稱為『偽裝』。」- 避免創建過濾搜尋引擎機器人的 mod_rewrite 指令。
搜尋引擎機器人喜歡 1:1 的內容:URI 映射(這是對內容連結進行排名的基礎) - 如果您使用 mod_rewrite 創建臨時重定向,或者您在多個 URI 下提供相同的內容,請考慮指定規範URI在您的 HTML 文件中。
是否存在 mod_rewrite 看似適合該工作但實際上並非如此的常見情況?
這本身就是一個巨大的(並且可能有爭議的)主題 - 更好(恕我直言)根據具體情況解決使用問題,並讓提問者確定建議的解決方案是否適合他們的需求。
有哪些常見的例子?
AskApache 的 mod_rewrite 技巧與技巧涵蓋了定期出現的幾乎所有常見用例,但是,給定用戶的「正確」解決方案可能取決於用戶配置和現有指令的複雜程度(這就是為什麼通常最好查看哪些其他每當出現 mod_rewrite 問題時,使用者就已經設定了指令)。
答案3
像許多管理員/開發人員一樣,我多年來一直在與複雜的重寫規則作鬥爭,並且對現有的 Apache 文件不滿意,因此我決定作為一個個人專案來深入了解mod_rewrite
Apache 的其餘部分實際工作和互動的方式strace
。
以下是重寫規則開發人員需要考慮的一些關鍵評論:
- 重寫的某些方面對於伺服器配置、虛擬主機、目錄、.htaccess 處理是常見的然而
- 根配置(伺服器配置、虛擬主機和目錄)的某些處理與 PerDir ( ) 處理截然不同
.htaccess
。 - 更糟的是,因為 PerDir 處理幾乎可以不加區別地觸發內部重定向循環,因此必須編寫根配置元素以意識到此類 PerDir 處理可以觸發此情況。
我想說的是,正因為如此,您幾乎需要將重寫用戶社群分為兩類,並將它們視為完全獨立的:
具有 Apache 設定 root 存取權限的人。這些通常是具有應用程式專用伺服器/VM 的管理員/開發人員,這裡的訊息非常簡單:
.htaccess
如果可能的話,請避免使用檔案;執行伺服器或虛擬主機配置中的所有操作。調試相當容易,因為開發人員可以設定調試並可以存取 rewrite.log 檔案。共享託管服務 (SHS) 的用戶。
- 此類用戶有使用
.htaccess
/Perdir 處理,因為沒有可用的替代方法。 - 更糟糕的是,這類用戶的技能水平(就使用 mod_rewrite 的正規表示式驅動的梯形邏輯而言)通常明顯低於經驗豐富的管理員。
- Apache 和託管提供者不提供偵錯/診斷支援。唯一的診斷訊息是成功的重定向,重定向到錯誤的 URI。或 404/500 狀態代碼。這讓他們感到困惑和無助。
- Apache 對於這個用例的重寫如何運作的解釋非常薄弱。例如,它沒有提供關於
.htaccess
選擇哪個 PerDir 檔案以及原因的明確解釋。它沒有解釋 PerDir 循環的複雜性以及如何避免這種情況。
- 此類用戶有使用
可能還有第三個群體:SHS 提供者的管理和支援人員,他們最終會涉足兩個陣營,並且必須承受上述後果。
我寫了幾篇文章風格的部落格文章(例如有關在 .htaccess 檔案中使用重寫規則的更多信息)其中涵蓋了許多細節點,為了保持這篇文章的簡短,我不會在這裡重複。我有自己的共享服務,並支援一些專用的 VM FLOSS 專案。我開始使用標準 LAMP VM 作為我的 SHS 帳戶的測試工具,但最終我發現最好做一個適當的鏡像 VM(描述這裡)。
然而,就管理社群應如何支援用戶而言.htaccess
,我認為我們需要開發並提供:
- 重寫系統在 PerDir 處理中實際運作方式的連貫描述
- 一組關於如何編寫
.htaccess
重寫規則的指南/最佳實踐 - 一個簡單的基於 Web 的重寫腳本解析器,有點類似於 W3C html 解析器,但使用者可以透過它輸入相同的測試 URI 或測試向量,並立即取得重寫邏輯流的日誌/
有關如何從規則中獲取內建診斷的提示(例如
- 利用將擴展反向引用($N 或 %N)
[E=VAR:EXPR]
的事實EXPR
,使它們可用作目標腳本的診斷。 如果您使用 [OR]、[C]、[SKIP] 和 [L] 標誌對重寫規則進行局部排序,以便整個重寫方案有效沒有需要利用內部重定向,那麼您可以添加以下規則作為規則 1 以避免所有循環麻煩:
RewriteCond %{ENV:REDIRECT_STATUS} !="" RewriteRule . - [L]
- 利用將擴展反向引用($N 或 %N)
答案4
編寫重寫規則時最常見的錯誤/陷阱是什麼?
一個非常容易陷入的陷阱是當您重寫 URL 來改變明顯的路徑時,例如 from
/base/1234/index.html
to /base/script.php?id=1234
。客戶端不會找到任何具有腳本位置相對路徑的圖像或 CSS。可以找到許多解決此問題的選項這個常見問題解答。