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