Apache-2.2

為什麼通過 HTACCESS 刪除 .PHP 擴展後 301 重定向不起作用?

  • June 10, 2019

我正在通過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 指令。(您可以在此處使用 MultiViewsmod_rewrite,但是,如果啟用 MultiViews,您可能會在嘗試重定向時遇到問題。)要確保禁用 MultiViews,請在文件頂部添加以下內容.htaccess

Options -MultiViews

但是,為了避免重定向循環,而不是匹配 against ,它會隨著 URL 的重寫而改變(與patternREQUEST_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 指令。

引用自:https://serverfault.com/questions/970701