Мой стек:
Исходная информация:
Недавно я запустил новый сайт для клиента. В процессе редизайна мы решили:
Пример СТАРОГО URL:
http://www.example.com/courses/acme-course.php
Пример НОВОГО URL:
https://www.example.com/courses/acme-course
Моя проблема:
Ненужное дополнительное перенаправление 301 происходит, когда пользователь переходит на один из СТАРЫХ URL-адресов.
Я не понимаю, почему создается дополнительное перенаправление 301, а не отправка пользователя напрямую на правильный целевой URL с использованием одного перенаправления 301.
Интересное наблюдение:
Ненужное дополнительное перенаправление 301 не происходит, когда я использую СТАРЫЙ URL-адрес с HTTPS вместо HTTP.
Пример:
https://www.example.com/courses/acme-course.php
_
Использование указанного выше URL-адреса приведет к единственному перенаправлению 301 на правильный целевой URL-адрес: https://www.example.com/courses/acme-course
Вот пример цепочки переадресации 301:
URL исходного запроса:
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 Redirect (правильный конечный целевой 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>
У вас есть две основные проблемы ...
.htaccess
файл. Ваш HTTP на HTTPS и www
канонические перенаправления должны быть удалены перед ваш фронт-контроллер, который направляет URL-адрес в вашу CMS. Следовательно, неправильное внешнее перенаправление на /index.php?url=courses/acme-course.php
- раскрытие вашей внутренней структуры URL-адресов CMS.Удаление .php
на самом деле не выполняется вашим .htaccess
директивы ?! Я предполагаю, что это должно быть сделано логикой вашего приложения / CMS? Следовательно, это будет всегда приведет ко второму перенаправлению (поскольку .htaccess
перенаправляет на HTTPS по тому же URL-пути). Вам нужно сделать что-то вроде следующего в верхней части вашего .htaccess
файл, чтобы удалить .php
расширение.
RewriteRule (.+)\.php$ https://www.example.com/$1 [R=301,L]
ОБНОВИТЬ: Если я изменю порядок правил / условий, останется ли мое размещение Options + FollowSymlink на прежнем уровне?
Это не имеет значения где то 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. Причина этой переменной вообще в том, что CMS может перенаправить на HTTP, если есть доступ к HTTP, или на HTTPS, если осуществляется доступ к HTTPS. Если вы форсируете HTTPS, это на самом деле не применимо. (Хотя этот env var может по-прежнему использоваться вашим приложением.)
Редко следует использовать NC
флаг при отрицательном условии. Поэтому я убрал его из условия !^www\.
. Вы хотите, чтобы он перенаправлял, когда хост не запускается с www.
- все строчные. С NC
флаг, что он не сможет перенаправить WwW.
- хотя это все равно было бы очень редко.
Я удалил ненужную проверку HTTPS при каноническом перенаправлении www.
В PT
флаг на последнем RewriteRule
не требуется в .htaccess
. В .htaccess
это поведение по умолчанию (сквозное).
Перед тестированием вам необходимо очистить кеш браузера, так как ошибочные перенаправления 301, скорее всего, были кэшированы браузером. По этой причине рекомендуется протестировать 302 (временных) перенаправления.