
我的堆疊:
- 燈
- 阿帕契/2.4.41
背景資訊:
我最近為客戶推出了一個新網站。在重新設計過程中,我們決定:
- 切換到全站 HTTPS
- 從 URL 中刪除 .php 副檔名
- 切換到 CMS
舊網址範例:
http://www.example.com/courses/acme-course.php
新網址範例:
https://www.example.com/courses/acme-course
我的問題:
當使用者導航到舊 URL 之一時,會發生不必要的額外 301 重定向。
我不明白為什麼要建立額外的 301 重定向,而不是使用單一 301 重定向將使用者直接發送到正確的目標 URL。
有趣的觀察:
當我使用帶有 HTTPS 而不是 HTTP 的舊 URL 時,不會發生不必要的額外 301 重定向。
例子:
https://www.example.com/courses/acme-course.php
_
使用上面的 URL 將正確執行單一 301 重新導向到正確的目標 URL:https://www.example.com/courses/acme-course
以下是 301 重定向鏈的範例:
原始請求網址:
http://www.example.com/courses/acme-course.php
1ST 301 重定向(不必要):
從:
http://www.example.com/courses/acme-course.php
到:
https://www.example.com/index.php?url=courses/acme-course.php
2ND 301 重定向(正確的最終目標 URL):
從:
https://www.example.com/index.php?url=courses/acme-course.php
到:
https://www.example.com/courses/acme-course
我的 .htaccess 程式碼:
# (1) General Settings
<IfModule mod_rewrite.c>
Options +FollowSymLinks
RewriteEngine On
</IfModule>
# (2) Force WWW
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} !=off
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{SERVER_ADDR} !=127.0.0.1
RewriteCond %{SERVER_ADDR} !=::1
RewriteRule ^ %{ENV:PROTO}://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
# (3) Force HTTPS
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
</IfModule>
# (4) URL Routing for CMS
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} =on
RewriteRule ^ - [env=proto:https]
RewriteCond %{HTTPS} !=on
RewriteRule ^ - [env=proto:http]
## Check if file/directory exists
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
## Route all other URLs to index.php/URL
RewriteRule ^(.*)$ index.php?url=$1 [PT,L,QSA]
</IfModule>
答案1
你有兩個主要問題......
- 您的指令在文件中的順序錯誤
.htaccess
。您的 HTTP 到 HTTPS 和www
規範重新導向需要進行前將 URL 路由到 CMS 的前端控制器。因此,錯誤的外部重定向到/index.php?url=courses/acme-course.php
- 暴露了您的內部 CMS URL 結構。
刪除
.php
其實不是按照您的.htaccess
指示執行的?我認為這一定是由您的應用程式/CMS 邏輯完成的?因此,這將總是導致第二次重新導向(因為.htaccess
重新導向到相同 URL 路徑上的 HTTPS)。您需要在檔案頂部執行類似以下操作.htaccess
來刪除.php
副檔名。RewriteRule (.+)\.php$ https://www.example.com/$1 [R=301,L]
更新:如果我重新排序規則/條件,我的選項 +FollowSymlinks 的位置是否保持不變?
這並不重要在哪裡該Options
指令發生。然而,將其放在頂部附近是合乎邏輯的(從可讀性的角度來看)。 (Apache 指令不一定會按照它們在設定檔中出現的順序執行,因為每個模組都是獨立工作的。)
假設您正在對文件進行手動編碼,.htaccess
那麼它可以被整理...
不需要(多個)
<IfModule mod_rewrite.c>
包裝器。 mod_rewrite 是可選的嗎?您的網站是否已移植到多個未啟用 mod_rewrite 的伺服器?不需要多個
RewriteEngine
指令。這最後的實際上實例獲勝並控制整個文件。多個
<IfModule>
區塊RewriteEngine
是由程式碼自動編輯和/或設計為在多個伺服器上未經編輯地運行的典型系統。
因此,您的.htaccess
文件應該按以下順序重寫:
Options +FollowSymlinks
# Enable the rewrite engine...
RewriteEngine On
# ----------------------------------------------------------------------
# | Forcing `https://` |
# ----------------------------------------------------------------------
# Redirect to HTTPS on the "same host" (requirement for HSTS)
RewriteCond %{HTTPS} !=on
RewriteRule (.*) https://%{HTTP_HOST}/$1 [R=301,L]
# ----------------------------------------------------------------------
# | Forcing `www` |
# ----------------------------------------------------------------------
RewriteCond %{HTTP_HOST} !^www\.
RewriteCond %{SERVER_ADDR} !=127.0.0.1
RewriteCond %{SERVER_ADDR} !=::1
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# ----------------------------------------------------------------------
# | URL Routing for CMS |
# ----------------------------------------------------------------------
# (3)
RewriteCond %{HTTPS} =on
RewriteRule ^ - [env=proto:https]
RewriteCond %{HTTPS} !=on
RewriteRule ^ - [env=proto:http]
# (4) - Check if physical file exists
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# (5) - Rewrite all other URLs to index.php/URL
RewriteRule (.*) index.php?url=$1 [L,QSA]
補充筆記:
環境
PROTO
變數包含所請求的任何協定。按照重定向的順序,現在總是 HTTPS。設定此變數的根本原因是,如果存取 HTTP,CMS 可以重定向到 HTTP;如果存取 HTTPS,則 CMS 可以重定向到 HTTPS。如果您強制使用 HTTPS,那麼它實際上並不適用。 (儘管您的應用程式可能仍會使用此環境變數。)您很少應該
NC
在否定條件下使用該標誌。這就是為什麼我將其從條件中刪除!^www\.
。您希望它在主機不以www.
- 全部小寫開頭時重定向。使用該NC
標誌,它將無法重定向WwW.
- 儘管這種情況非常罕見。我已經刪除了 www 規範重新導向上不必要的 HTTPS 檢查。
PT
最後的標誌在RewriteRule
中不是必需的.htaccess
。這.htaccess
是預設行為(通過)。在測試之前,您需要清除瀏覽器緩存,因為錯誤的 301 重定向可能已被瀏覽器快取。因此,最好使用 302(臨時)重定向進行測試。