Перезапись Apache вызывает бесконечную рекурсию, если запрашивается подкаталог

У меня есть правило перезаписи, которое работает большую часть времени. Однако, если я запрашиваю страницу с несуществующим подкаталогом, и подкаталог соответствует существующему файлу, это вызывает бесконечную рекурсию.

    Options +FollowSymLinks
    AllowOverride None
    Require all granted

    RewriteEngine on

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME}\.php -f
    RewriteRule ^(.*)$ $1.php [L]

Это работает, если я запрашиваю:

  • test.php
  • test ] (обслуживает test.php)
  • badname.php или badname (дает 404 правильно)
  • directory / file_that_exists или directory / file_that_exists. php

Все работает. Но если существует test.php , и я запрашиваю test / test , это вызывает бесконечную рекурсию. Журнал ошибок выглядит так:

[Wed May 23 08:16:28.176564 2018] [core:debug] [pid 27054] core.c(3620): [client 0.0.0.0:57764] AH00122: redirected from r->uri = /test/test.php.php.php
[Wed May 23 08:16:28.176566 2018] [core:debug] [pid 27054] core.c(3620): [client 0.0.0.0:57764] AH00122: redirected from r->uri = /test/test.php.php
[Wed May 23 08:16:28.176568 2018] [core:debug] [pid 27054] core.c(3620): [client 0.0.0.0:57764] AH00122: redirected from r->uri = /test/test.php
[Wed May 23 08:16:28.176570 2018] [core:debug] [pid 27054] core.c(3620): [client 0.0.0.0:57764] AH00122: redirected from r->uri = /test/test

Что я делаю не так? Разве он не должен потерпеть неудачу, когда увидит, что test / test.php не существует? ( RewriteCond% {REQUEST_FILENAME} \. Php -f ) Я получаю тот же результат, если просто добавляю косую черту, например test / .

1
задан 23 May 2018 в 19:27
2 ответа

Разве он не должен потерпеть неудачу, когда увидит, что test / test.php не существует? ( RewriteCond% {REQUEST_FILENAME} \. Php -f ) Я получаю тот же результат, если просто добавляю косую черту, например test /

. Если бы эта проверка выполнялась, это не помогло бы, но это не так.

Переменная сервера REQUEST_FILENAME содержит путь к файловой системе после URL был сопоставлен с файловой системой. Это не обязательно то же самое, что URL-путь, с которым соответствует шаблон RewriteRule .

Например, в вашем случае при запросе /test/test.php , где нет физического подкаталога с именем / test , серверная переменная REQUEST_FILENAME содержит строку вида / absolute / filesystem / path / to / test ( т. е. запрошенный URL до последнего известного каталога + первый сегмент пути / test ). И URL-путь, соответствующий шаблону RewriteRule , будет /test/test.php (к которому вы будете многократно добавлять .php , потому что /test.php существует,как проверено предыдущей директивой RewriteCond ).

Вы можете решить эту проблему, вместо этого проверив URL-путь (построив абсолютный путь файловой системы для проверки), как предлагает @shanew. Например:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/$1\.php -f
RewriteRule ^(.*)$ $1.php [L]

Оптимизация, позволяющая избежать всех этих проверок файловой системы для каждого запроса, будет включать проверку расширения .php .css , . js и т. д.), прежде чем проверять, существуют ли файлы:

RewriteCond %{REQUEST_URI} !\.(php|css|js|jpg|png)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/$1\.php -f
RewriteRule ^(.*)$ $1.php [L]

Это позволяет избежать ненужных проверок, когда запрошенный URL уже заканчивается на .php , .css и т. д.

2
ответ дан 3 December 2019 в 18:27

Если вы делаете это в разделе каталога или в файле .htaccess, вы сможете остановить бесконечную рекурсию, добавив параметр END в свое RewriteRule, чтобы оно выглядело так

RewriteRule ^ (. *) $ 1.php [L, END]

Это останавливает механизм перезаписи от выполнения любой последующей обработки перезаписи (тогда как L просто останавливает текущий раунд обработки перезаписи). Документация Apache 2.4 Rewrite Flags объясняет более подробно.

Тем не менее, я не уверен, что это приведет вас к тому поведению, которое вам действительно нужно, поскольку оно все равно изменит / test / test на / test / test.php, несмотря на то, что его не существует. Чтобы решить эту проблему, я думаю, вам нужно тестировать весь путь к файлу, а не только имя файла. Для этого замените:

% {REQUEST_FILENAME}

на

% {DOCUMENT_ROOT}% {REQUEST_URI}

Кроме того, на всякий случай я бы добавил еще один RewriteCond вверху, чтобы правило никогда не срабатывает, если имя файла уже оканчивается на .php (указание его первым означает, что другие условия можно пропустить, если он возвращает true):

% {REQUEST_FILENAME} "! \. php $"

1
ответ дан 3 December 2019 в 18:27

Теги

Похожие вопросы