Назад | Перейти на главную страницу

Правило Apache 2.4 mod_rewrite, применяющее перенаправление к правилу без перенаправления (только флаг [L])

Итак, у меня возникает эта сумасшедшая проблема на 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 является:

  1. Первоначальное перенаправление действительно пропускается из-за состояние который явно исключает этот единственный URL.
  2. Запрос внутренне переписанный к index.php по последнему правилу.

Теперь процесс перезаписи начинается заново, с перезаписанным URL (т.е. http://app.example.com/index.php):

  1. http://app.example.com/index.php "перенаправлен" на https://app.example.com/index.php по первому правилу. Из-за L флаг, текущий цикл обработки останавливается (т. е. оставшиеся 2 правила перезаписи пропускаются).

  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]