Я установил .htaccess
для перенаправления запросов к содержащему каталогу /htdocs/foo
в файл bar.php
.
Во-первых: я знаю, как этого добиться, но по независящим от меня причинам .htaccess
также хочет проверить, разрешается ли URL-адрес в обычный файл, и перенаправляет, если нет.
Звучит не слишком сложно, но я не могу с этим разобраться. : [
Это мое .htaccess
:
RewriteRule ^$ http://www.example.com/foo/bar.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* doh.php
Эти правила не перенаправляют www.example.com/foo
к bar.php
но doh.php
вместо.
Я проверил error.log
и по какой-то причине условие перезаписи выполняется, хотя файл bar.php
настоящее.
RewriteCond: input='http://www.example.com/foo/bar.php' pattern='!-f' => matched
Видимо Apache не считает bar.php
как обычный файл, если на него указывает переписанный URL. www.example.com/foo/bar.php
работает как положено.
Есть идеи, почему?
Редактировать # 1:
Для пояснения это структура файла:
/ (File system root)
|
+ htdocs (Document root)
|
+ foo
|
+ .htaccess
+ bar.php
+ doh.php
Редактировать # 2:
Я использую Apache 2.4.
Решение:
В RewriteCond
надеется REQUEST_FILENAME
содержать местный путь. Из-за отсутствия L
отметить RewriteRule
наборы REQUEST_FILENAME
на URL-адрес, который, очевидно, не является локальным путем, поэтому проверка на !-f
Спички.
В конечном итоге, прикрепив L
флаг к RewriteRule
производит желаемое поведение:
RewriteRule ^$ http://www.example.com/foo/bar.php [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* doh.php
После перезаписи URL на http://www.example.com/foo/bar.php
запускается новый цикл процесса, переназначающий перезаписанный URL на локальный путь /htdocs/foo/bar.php
что не соответствует RewriteCond
с !-f
тест. И /htdocs/foo/bar.php
проходит. :)
RewriteCond: input='/htdocs/foo/bar.php' pattern='!-f' => matched
Не уверен, что это было чрезмерно проиллюстрировано, но если не происходит «чего-то еще», это не так, как ожидалось, учитывая приведенные выше директивы.
Поскольку в вашем первоначальном (подразумеваемом) перенаправлении отсутствует L
флаг, обработка немедленно переходит к следующей директиве. На этом этапе я ожидал увидеть в журналах что-то вроде следующего:
RewriteCond: input='http://www.example.com/foo/bar.php' pattern='!-f' => matched
Сразу после первого RewriteRule
, то REQUEST_FILENAME
директива обновляется как есть для вывода последнего RewriteRule
т.е. http://www.example.com/foo/bar.php
. На данном этапе он не отображается в файловой системе. (Если вы не указали относительный путь замена, и в этом случае префикс каталога будет добавлен в начало - что похоже на то, что вы видите в журналах - но это не соответствует директиве в вопросе.)
Таким образом, вышеуказанное условие действительно "соответствует" - "абсолютный URL" не может существовать как файл в файловой системе.
Затем вы получите испорченный 302 "переписать" на doh.php
. Запрос внутренне переписанный к doh.php
, но статус HTTP установлен на 302 из более раннего "перенаправления", но нет Location
заголовок, поэтому нет внешнего перенаправления. (Хотя вы, кажется, подразумеваете, что существует "перенаправление" на doh.php
?)
RewriteRule ^$ http://www.example.com/foo/bar.php
Чтобы запустить внешнее перенаправление (с учетом более поздней перезаписи), вам необходимо включить L
(last
), чтобы остановить текущий раунд обработки.
Вы также должны быть явными и включать R
флаг. Здесь подразумевается перенаправление 302, потому что вы явно включили схему + имя хоста в RewriteRule
.
Например:
RewriteRule ^$ http://www.example.com/foo/bar.php [R,L]
В стороне:
Эти правила не перенаправляют
www.example.com/foo
Обратите внимание, что если вы запросите /foo
, без косой черты в конце и /foo
это физический каталог, тогда mod_dir сначала неявно 301 перенаправит на /foo/
чтобы «исправить» URL, добавив в конце косую черту.