為什麼通過 HTACCESS 刪除 .PHP 擴展後 301 重定向不起作用?
我正在通過Apache/2.2.26 (Unix)從我網站 ( )上
.php
特定目錄中包含的文件中刪除副檔名。courses``.htaccess
另外,我想 301 將舊
.php
版本重定向到非 php 版本。舊網址結構:
http://www.example.com/courses/blue-course.php
新的 URL 結構:
http://www.example.com/courses/blue-course
我目前的問題:
- 非
.php
版本的頁面不是 301 重定向。- 頁面的兩個版本
.php
和非.php
版本都是可見的。這是我的程式碼:
RewriteEngine On # External Routing RewriteCond %{REQUEST_URI} (.*\/courses\/)([^\s]+)\.php [NC] RewriteRule (.*courses\/)([^\s]+)\.php http://www.example.com/$1$2 [L,R=301] # Internal Routing RewriteCond %{REQUEST_FILENAME}.php -f RewriteRule ^ %{REQUEST_URI}.php [L]
而不是“不是 301 重定向”,我希望您的程式碼創建一個重定向循環。因為前面的重定向也會擷取重寫的 URL(通過後面的指令)和重定向等等。
非
.php
版本的頁面不是 301 重定向。這不正確嗎?還是應該在這裡刪除“非”或“非”?
頁面的兩個版本
.php
和非.php
版本都是可見的。這似乎表明 MultiViews 可能已啟用。需要禁用 MultiViews 才能正確執行您的 mod_rewrite 指令。(您可以在此處使用 MultiViews或mod_rewrite,但是,如果啟用 MultiViews,您可能會在嘗試重定向時遇到問題。)要確保禁用 MultiViews,請在文件頂部添加以下內容
.htaccess
:Options -MultiViews
但是,為了避免重定向循環,而不是匹配 against ,它會隨著 URL 的重寫而改變(與pattern
REQUEST_URI
匹配的 URL-path 相同),您應該匹配 against (它包含請求的第一行並且不會更改)或者只需檢查環境變數是否為空,以確保您只檢查初始請求而不是重寫請求。RewriteRule
THE_REQUEST``REDIRECT_STATUS
例如:
# External Routing RewriteCond %{ENV:REDIRECT_STATUS} ^$ RewriteRule (.*courses\/)([^\s]+)\.php http://www.example.com/$1$2 [L,R=301]
注意:始終使用 302(臨時)重定向進行測試以避免記憶體問題。
但是,您的正則表達式可以稍微清理一下。根據您的範例 URL,
/courses
是第一個路徑段,但是,您的正則表達式匹配 URL 路徑中的任何位置的“課程/”。而且您允許重定向任何帶有路徑資訊的 URL - 這是故意的嗎?您只需要一個反向引用;不是兩個。並且沒有必要在RewriteRule
pattern中轉義斜線。除非您有多個域,否則您不一定需要替換字元串中的方案+主機名?因此,這也許可以“簡化”為:
# External Routing RewriteCond %{ENV:REDIRECT_STATUS} ^$ RewriteRule ^(courses/[^\s]+)\.php$ /$1 [L,R=301]
# Internal Routing RewriteCond %{REQUEST_FILENAME}.php -f RewriteRule ^ %{REQUEST_URI}.php [L]
這可能適用於您正在測試的特定 URL,但是,此規則很容易破壞(並且它不是專門針對
/courses
子目錄)。%{REQUEST_FILENAME}.php
不一定相同%{REQUEST_URI}.php
。因此,雖然條件可能成功,但您仍然可能最終重寫為無效的 URL,這可能導致無限循環 - 500 Internal Server Error。例如,給定一個請求
/courses/blue-course/foo
(基於您的範例), where/courses
是文件系統上的一個目錄,並且blue-course.php
是您打算重寫的文件(沒有子目錄/blue-course
),並且/foo
只是被錯誤地添加到 URL 的內容(也許甚至被外部第三方惡意),那麼這將導致 500 錯誤。因為%{REQUEST_FILENAME}.php
解析為<document-root>/courses/blue-course.php
(存在)但%{REQUEST_URI}.php
解析為/courses/blue-course/foo.php
(不存在)。然後重寫過程重新開始,導致無限循環。這可以通過將規則調整為以下內容來解決:
# Internal Routing RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f RewriteRule ^courses/ %{REQUEST_URI}.php [L]
假設您沒有多個文件副檔名或文件基名中的點,例如。
blue-course.abc.php
那麼這也可以通過確保請求的 URL 沒有文件副檔名來優化(以避免測試任何靜態資源)。例如:# Internal Routing RewriteCond %{REQUEST_URI} !\.\w{2,4}$ RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f RewriteRule ^courses/ %{REQUEST_URI}.php [L]
CondPattern上的
!
前綴否定了它的含義。總之
Options -MultiViews RewriteEngine On # External Routing RewriteCond %{ENV:REDIRECT_STATUS} ^$ RewriteRule ^(courses/[^\s]+)\.php$ /$1 [L,R=301] # Internal Routing RewriteCond %{REQUEST_URI} !\.\w{2,4}$ RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f RewriteRule ^ %{REQUEST_URI}.php [L]
上面的指令假定您正在使用
.htaccess
站點文件根目錄中的文件。但是,如果您只需要以特定目錄為目標,並且需要從父配置繼承的其他 mod_rewrite 指令最少,那麼.htaccess
在該子目錄中創建一個附加文件以保持配置獨立是有益的。但是,需要稍微更改指令:RewriteEngine On # External Routing RewriteCond %{ENV:REDIRECT_STATUS} ^$ RewriteRule ^([^\s]+)\.php$ /courses/$1 [L,R=301] # Internal Routing RewriteCond %{REQUEST_URI} !\.\w{2,4}$ RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f RewriteRule ^ %{REQUEST_URI}.php [L]
請注意,這將(預設情況下)完全覆蓋父配置(目錄上下文)中的任何 mod_rewrite 指令。