У меня есть правило перезаписи, которое работает большую часть времени. Однако, если я запрашиваю страницу с несуществующим подкаталогом, и подкаталог соответствует существующему файлу, это вызывает бесконечную рекурсию.
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/
.
Разве он не должен потерпеть неудачу, когда увидит это
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 $"