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

Apache не соответствует переписанному URL-адресу как обычному файлу

Я установил .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, добавив в конце косую черту.