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

Перезапись 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, это вызывает бесконечную рекурсию. Журнал ошибок выглядит так:

[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/.

Разве он не должен потерпеть неудачу, когда увидит это 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, и т.д.

Если вы делаете это в разделе каталога или в файле .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 $"