Итак, у меня возникает эта сумасшедшая проблема на apache 2.4 / CentOS7 с веб-приложением.
Я хочу перенаправить все незащищенные URL-адреса в домене приложения на защищенные версии, ЗА ИСКЛЮЧЕНИЕМ для одного URI (конечная точка веб-перехватчика для режима песочницы поставщика - нет действительного сертификата SSL, поскольку это не производственная среда, поэтому мы должны разрешить HTTP). В дополнение к домену приложения существуют домены сайтов клиентов, которые также проходят через него, поэтому мы НЕ хотим, чтобы они перенаправлялись, поскольку ни у одного из них нет SSL, поэтому мы явно сопоставляем HTTP_HOST.
Это шаблон переднего контроллера, мы хотим index.php
для обработки всех этих запросов к нефайловым и некаталогам для всех доменов и через SSL только для домена приложения.
Это КАЖЕТСЯ так просто:
RewriteEngine on
RewriteBase "/"
# Redirect all app domain urls to ssl
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} "^app\.example\.com$" [NC]
RewriteCond %{REQUEST_URI} "!^/AllowThisUri/Http"
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# I had to split this -d rule off of the -f rule to account for custom
# domain homepages from not being routed to the front controller when
# transitioning from centos6/apache2.2 for some reason
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_URI} "!^/$"
RewriteRule "^(.*)$" index.php [L]
# Redirect non-files to entry script
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule "^(.*)$" index.php [L]
Но что происходит, когда я тестирую
curl http://app.example.com/AllowThisUri/Http
Последнее правило, которое нужно передать index.php
используется, но вместо того, чтобы обрабатывать его index.php
, он перенаправляет туда ... curl возвращает (сокращенный ответ)
<title>301 Moved Permanently</title>
<p>The document has moved <a href="https://app.example.com/index.php">here</a>.</p>
Итак, если я закомментирую это новое исключение RewriteCond
:
#RewriteCond %{REQUEST_URI} "!^/AllowThisUri/Http"
Произошло ожидаемое: он перенаправляет на тот же URL-адрес, кроме https:
<title>301 Moved Permanently</title>
<p>The document has moved <a href="https://app.example.com/AllowThisUri/Http">here</a>.</p>
Если я полностью удалю перенаправление на логику HTTPS, я получу ответ от фронт-контроллера, которого я ожидал (он просто печатает "привет" для тестирования).
#RewriteCond %{HTTPS} off
#RewriteCond %{HTTP_HOST} "^app\.example\.com$" [NC]
#RewriteCond %{REQUEST_URI} "!^/AllowThisUri/Http"
#RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
Я вставил все это в точности так, как мы начали, и могу изменить R=301
к 302
вот так:
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=302,L]
и, конечно же, он меняет плохой неожиданный ответ перенаправления:
<title>302 Found</title>
<p>The document has moved <a href="https://app.example.com/index.php">here</a>.</p>
Я надеюсь это имеет смысл. Объяснять, прошу прощения, многословно.
Я вижу, хотя !^/AllowThisUri/Http
условие совпадает, и мы пропускаем правило перенаправления HTTPS, и правила продолжают обрабатываться к тому времени, когда оно дойдет до последнего правила, отправившего его index.php
это похоже на то, что часть ПРАВИЛА ПРОПУСКАЕМЫХ правил перенаправления кажется каким-то образом зависает и влияет на результат. Для меня это не имеет абсолютно никакого смысла.
Повторюсь, не исключая одного URI с RewriteCond
, перенаправления без ssl на ssl работают должным образом. Чтобы полностью удалить всю эту логику перенаправления, index.php
передний контроллер отвечает ожидаемым ответом. Это когда мы добавляем еще один RewriteCond
чтобы исключить один URI, который ведет себя неверно / неожиданно и возвращает перенаправление на index.php
вместо обработки и возврата ожидаемого ответа от фронт-контроллера.
Может быть, кто-то знает какие-то непонятные настройки или, может быть, вещь apache 2.4, которая может вызвать это?
«Проблема» в том, что процесс перезаписи не заканчивается, когда доходит до последней директивы в каталог контекст (.htaccess
?). Процесс перезаписи начинается заново и повторяется до тех пор, пока URL-адрес не пройдет без изменений или не произойдет перенаправление.
Итак, что происходит, когда вы запрашиваете http://app.example.com/AllowThisUri/Http
является:
index.php
по последнему правилу.Теперь процесс перезаписи начинается заново, с перезаписанным URL (т.е. http://app.example.com/index.php
):
http://app.example.com/index.php
"перенаправлен" на https://app.example.com/index.php
по первому правилу. Из-за L
флаг, текущий цикл обработки останавливается (т. е. оставшиеся 2 правила перезаписи пропускаются).
Поскольку было инициировано перенаправление (код ответа 3xx), процесс перезаписи не начинается заново, и отправляется ответ перенаправления.
Одно из решений - запускать перенаправление только для прямых запросов от клиента, а не для перезаписанных запросов (на index.php
). Для этого мы можем добавить к редиректу дополнительное условие, которое проверяет REDIRECT_STATUS
переменная окружения. Первоначально эта переменная пуста и после первой успешной перезаписи ей присвоено значение «200».
Например:
# Redirect all app domain urls to ssl
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} "^app\.example\.com$" [NC]
RewriteCond %{REQUEST_URI} "!^/AllowThisUri/Http"
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
(Небольшая уборка ... не нужно снимать RewriteRule
шаблон если обратная ссылка не используется.)
Если есть только 1 URL-адрес, который вы хотите исключить, более эффективно проверить это в RewriteRule
шаблон, вместо использования условия и проверки на соответствие REQUEST_URI
переменная сервера (если этот же URL не используется и для других имен хостов?). Например:
# Redirect all app domain urls to ssl
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} ^app\.example\.com$ [NC]
RewriteRule !^AllowThisUri/Http https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]