擴展刪除重寫後存在正版目錄時,Apache 重寫導致伺服器錯誤 403
我花了幾天時間嘗試創建一個特定的規則集,該規則集允許我
.html
從目錄中的所有文件中刪除副檔名並呈現更簡潔的 URI。我正在使用.htaccess
該網站根目錄中的一個文件,並且計劃在許多存在相同問題的網站上使用它。我經歷了許多類似配置的迭代,但我發現的最接近的實際上是直接從這裡的文章中刪除的(遺憾的是,我無法評論以了解更多資訊)。所以以下是我目前擁有的:
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f RewriteRule (.*) $1.html [L] ErrorDocument 404 https://example.com/404
它很簡單並且在大多數情況下工作得很好,但是當一個真正的目錄存在時,它似乎會拋出一個 403 伺服器錯誤。
例如,如果我訪問過
example.com/directory_A
- 我會收到 403 錯誤。但是,根目錄中實際上有一個同名的文件,所以我希望它實際存在example.com/directory_A.html
(但html
當然沒有)。目錄中directory_A
有一個文件,file_B.html
,訪問按預期example.com/directory_A/file_B
呈現file_B.html
內容。我正在繞圈子 - 這絕對是我最接近解決我的問題,但我只是不知道足以讓我克服最後一個障礙,所以這裡的任何幫助將不勝感激。
但是當一個真正的目錄存在時,它似乎會拋出一個 403 伺服器錯誤。
403 不是由您發布的規則引起的。無論如何,第一個條件明確排除了目錄,因此它甚至沒有被處理。
403 是由 mod_dir 試圖從子目錄提供
DirectoryIndex
文件(例如index.html
)引起的/directory_A/
- 這可能不存在。具體來說,當您請求
/directory_A
(沒有尾部斜杠)時,mod_dir 將通過 301(永久)重定向附加尾部斜杠來“修復”URL。然後,在重定向請求中,mod_dir 嘗試從該目錄提供目錄索引,如果它不存在並且目錄列表被禁用(mod_autoindex),則觸發 403。
DirectorySlash Off
要按照您的要求進行操作,您需要防止 mod_dir 使用指令在物理目錄上附加斜杠。然後,為了提供服務/directory_A.html
(而不是通過請求) ,/directory_A
您需要刪除排除目錄請求的第一個條件。例如:
# Ensure that directory listings are disabled Options -Indexes # Prevent mod_dir appending a slash to physical directories DirectorySlash Off # Rewrite request to append ".html" extension if it exists RewriteCond %{DOCUMENT_ROOT}/$1.html -f RewriteRule (.*) $1.html [L]
請注意,如果您要設置目錄列表,則必須禁用目錄列表,否則 mod_autoindex 將在請求沒有尾部斜杠的目錄並且相應文件不存在
DirectorySlash Off
時生成目錄列表。.html
請注意 Apache 文件中關於DirectorySlash
指令的安全警告。在
RewriteCond
指令中,我將使用更改為使用模式REQUEST_URI
中的反向引用,而不是保持一致 - 以確保您始終在TestString和替換中使用相同的值。RewriteRule
RewriteCond
RewriteRule
請注意,請求
/directory_A/
(帶有尾部斜杠)仍將導致 403 響應,但這是預期的,除非您特別想處理這種邊緣情況並將請求路由到/directory_A.html
而不是?**更新:**最好通過實現外部重定向來實現.html
,以便在存在相應文件時簡單地從 URL 中刪除尾部斜杠,因此重寫(上面)然後執行其操作並將.html
副檔名附加到重定向響應中。這可確保您擁有一個規範的 URL,避免潛在的重複內容問題(其中/directory_A
和/directory_A/
兩者都返回相同的資源)。例如,在上述“重寫”規則之前添加以下“重定向”規則:
# Remove trailing slash on URL-path when the corresponding ".html" file exists RewriteCond %{DOCUMENT_ROOT}/$1.html -f RewriteRule (.*)/$ /$1 [R=302,L]
這不會顯式檢查目錄,因此它也適用於其他“文件”。例如。
/directory_A/file_B/
將被重定向到/directory_A/file_B
(刪除斜杠)。首先使用 302(臨時)重定向進行測試,並且只有在您確定它按預期工作時才更改為 301(永久)重定向以避免潛在的記憶體問題。
您需要確保在測試之前清除瀏覽器記憶體,因為 mod_dir 觸發以在目錄上附加尾部斜杠的早期 301 將被瀏覽器記憶體。
TBH,在實現“無擴展”URL 時,最好避免此類衝突,並且不要將文件與物理目錄具有相同的基本名稱。
在旁邊:
優化
.html
您可以優化附加副檔名的指令,因為它目前正在測試每個請求是否存在.html
最後的文件(這相對昂貴並且可能沒有必要)。例如。請求/images/myimage.jpg
,您的規則將檢查/images/myimage.jpg.html
文件系統上是否存在。您可以通過排除已經包含文件副檔名的請求來避免這些不必要的檢查(假設您的 URL 沒有故意在看起來像文件副檔名的 URL 路徑末尾附近有點)。例如:
# Rewrite request to append ".html" extension if it exists RewriteCond $1 !\.\w{2,4}$ RewriteCond %{DOCUMENT_ROOT}/$1.html -f RewriteRule (.*) $1.html [L]
錯誤文件
ErrorDocument 404 https://example.com/404
該指令可以說是不正確的。
- 當您指定絕對 URL 時,它將觸發錯誤文件的 302(臨時)重定向,而不是應有的內部子請求。因此,除非您在重定向響應中手動設置,否則客戶端不會看到 404 HTTP 狀態。但無論哪種方式,客戶端都會首先看到 302。
- 您應該在此處指定 404 錯誤文件的實際 URL,而不是“無擴展”版本(需要進行額外處理),就像您在此處所做的那樣。這完全在您的伺服器內部,客戶端看不到此 URL。
例如:
ErrorDocument 404 /404.html
儘管通常最好將錯誤文件放在單獨的子目錄中,以便從其他重定向/重寫中排除。例如。
/errordocs/404.html
.