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

HTTPS / HTTP правила mod_rewrite, странное взаимодействие с последующей перезаписью CMS index.php

С приведенными ниже изменениями в htaccess, как и ожидалось, этот запрос HTTPS не переписано:

https://example.com/system/anything

не является переписан на

http://example.com/system/anything

Но неожиданно этот HTTPS-запрос переписывается:

https://example.com/preview/anything

является переписан на

http://example.com/index.php/preview/anything

Почему это?

Еще пара фактов / наблюдений:

/system/ это фактический путь на сервере. Но /preview/ не является фактическим путем - это сегмент URL, который имеет смысл в CMS, например, /index.php/preview/anything как CMS получает запрос на /preview/anything URL.

Другие несистемные URL-адреса перезаписываются правильно (с HTTPS на HTTP) и правильно передаются в index.php. Например.,

https://example.com/real

является переписан на

http://example.com/real

Вот полный блок правил:

 <IfModule mod_rewrite.c>
 RewriteEngine On
 RewriteBase /

 # Force HTTPS for System URLs
 RewriteCond %{REQUEST_URI} ^/system(.*)$ [NC]
 RewriteCond %{HTTPS} !=on
 RewriteRule ^(.*)$ "https://example.com/$1" [R=301,L]

 # Force HTTP for Other URLs, but not: system or preview
 RewriteCond %{REQUEST_URI} !^/(system|preview)/(.*)$ [NC]
 RewriteCond %{HTTPS} =on
 RewriteRule ^(.*)$ "http://example.com/$1" [R=302,L]

 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d
 RewriteRule ^(.*)$ /index.php/$1 [L]
 </IfModule>

Любое понимание того, почему /preview/ получает странное обращение?


Добавлено: Обратите внимание, что /preview/anything получает 302 перенаправить на /index.php/preview/anything - и ЭТО большая часть того, что кажется таким странным / неожиданным. В окончательном правиле не должно быть перенаправления, просто переписывание.

Эти правила перезаписи в .htaccess файл? В таком случае в [L] флаг не делает то, что вы думаете- он останавливает обработку текущего набора правил, но затем запрос снова обрабатывается Apache, используя .htaccess файлы, подходящие для перезаписанного URI, поэтому ваши правила могут быть выполнены снова. Этого не происходит для правил, которые находятся в файле конфигурации Apache (а не внутри <Directory> раздел) - в этом случае [L] flag обрабатывается должным образом.

Для вашего примера, https://example.com/preview/anything внутренне переписан на https://example.com/index.php/preview/anything по третьему правилу; однако для обработки этого запроса Apache должен прочитать .htaccess файл снова - и на этот раз URI соответствует вашему второму правилу, которое возвращает 302 перенаправить.

Apache 2.4.x поддерживает [END] флаг, который останавливает такие циклы перезаписи, в отличие от [L]; решения для более ранних версий Apache более сложны.

Если вы хотите убедиться, что Apache выполняет только один проход по вашим правилам перезаписи, вы можете добавить следующее правило перед всеми остальными:

RewriteCond %{ENV:REDIRECT_STATUS} !=""
RewriteRule ^ - [L]

На первом проходе REDIRECT_STATUS будет пусто; на втором проходе он будет иметь непустое значение (обычно 200), и правило будет соответствовать и действительно прекратит дальнейшие перезаписи.

В случае, если такое правило не подходит (например, в некоторых случаях вам нужно обрабатывать перезаписанные URI на втором проходе), вы можете установить переменную среды в правилах, которые должны быть действительно окончательными:

RewriteRule ^(.*)$ /index.php/$1 [L,E=FINISH:1]

и добавьте следующее правило перед всеми остальными:

RewriteCond %{ENV:REDIRECT_FINISH} !=""
RewriteRule ^ - [L]

Обратите внимание, что во время второго прохода Apache добавляет REDIRECT_ к именам переменных среды, которые были определены во время первого прохода, поэтому вам необходимо установить FINISH, но тест REDIRECT_FINISH.

В качестве альтернативы вы можете попытаться изменить условия сопоставления так, чтобы на втором проходе URI, измененные на первом проходе, не совпадали (например, вставьте (index\.php/)? в регулярное выражение во втором правиле).

Это http схема (вместо https) что тебя беспокоит? Если это так, вам следует посмотреть на RewriteBase директива.

Из mod_rewrite документы:

В RewriteBase директива определяет префикс URL, который будет использоваться для каждого каталога (htaccess) RewriteRule директивы, заменяющие относительный путь.

Эта директива требуется, когда вы используете относительный путь в подстановке в контексте для каждого каталога (htaccess) (...)

Похоже, последнее правило вводит абсолютное httpперенаправление на основе, когда оно выполняется, возможно, из-за какой-либо ошибки или функции в Apache или mod_rewrite.

Как насчет того, чтобы разбить это на два правила с дополнительным условием?

Попробуй это:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{HTTPS} =off
RewriteRule ^(.*)$ http://example.com/index.php/$1 [L]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{HTTPS} =on
RewriteRule ^(.*)$ https://example.com/index.php/$1 [L]

Я не думаю, что вам нужен '(. *) $' В конце операторов RewriteCond, потому что вы нигде не используете захваченные данные. Вы можете упростить эти 2 так:

RewriteCond %{REQUEST_URI} ^/system [NC]

и RewriteCond% {REQUEST_URI}! ^ / (system | предварительный просмотр) / [NC]

Я бы также порекомендовал включить RewriteLog и установить RewriteLogLevel, чтобы точно видеть, что apache делает для каждого запроса. С apache 2.2 или ниже это будет:

RewriteLog /var/log/apache2/rewrite.log
RewriteLogLevel 7

Вы должны шаг за шагом увидеть, какие именно совпадения и какие действия предпринял apache.