mod_rewrite: просмотр списка правил только один раз

У модуля mod_rewrite сервера «Апач» есть особенность, которая иногда портит много крови — после каждого переписывания URL, он просматривает список правил снова. Если ничего не предпринять, иногда происходит зацикливание, что ввергает новичков в ступор.

Мне эта особенность ни разу не пригодилась, но избавляться от неё приходится постоянно. В комментариях на «Хабре» к статье, название которой я вынес в заголовок, подсказали очень хороший способ, добавить первым правилом следующее:
# Don't loop.
RewriteCond %{ENV:REDIRECT_STATUS} !^$
RewriteRule .* - [L]
Если перезапись URL уже происходила, то любой URL оставляем без изменений и следующего цикла обработки уже не происходит.
21 ноября 2009 15:24

astur (astur.net.ru)
21 ноября 2009, 18:11

Вуду, однако... :)

bolk (bolknote.ru)
21 ноября 2009, 18:26, ответ предназначен astur (astur.net.ru):

Mod_rewrite довольно понятная фиговина, на деле. Но язык крайне примитивный, делался чтобы просто было написать модуль, а не правила на нём. То, что есть в nginx выглядит и читается лучше.

Выше просто проверяется на непустоту переменная окружения (об этом говорит «EVN:») «REDIRECT_STATUS» (!^$ — это просто «не пустая строка», где «^$» — пустая регулярка, где написано «начало и конец строки»).

Если она не пустая, то разрешается сработать правилу ниже (правило записывается через директиву RewriteRule), в правиле написана регулярка («.*» — «всё что угодно, включая пустоту») и во что она переписывается («-» — это «оставить как было»). «[L]» означает, что правила ниже применять не нужно.

Поскольку, в случае срабатывания RewriteCond и RewriteRule строка URL остаётся без изменений (у нас же «-» указано), то правило «после каждого переписывания URL, он просматривает список правил снова» не выполняется — ведь URL мы переписывали.

astur (astur.net.ru)
21 ноября 2009, 19:50

Да не, принцип-то понятен, а вот додуматься до такого простого костыля... я год назад решал похожую проблему и так и сдался :(

...а nginx - это да. У него и возможности по реврайтам апачу не уступают. Видимо из-за этого буду переходить на него с lighttpd.

bolk (bolknote.ru)
21 ноября 2009, 19:56, ответ предназначен astur (astur.net.ru):

Ну, традиционное решение — делать ещё правила для URL, которых не надо перезаписывать. Например:

RewriteRule ^/index\.php$ - [L]
RewriteRule .* /index.php [L]

Lynn «Кофеман» (alexeyten.ya.ru)
21 ноября 2009, 23:16

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

bolk (bolknote.ru)
22 ноября 2009, 00:31, ответ предназначен Lynn «Кофеман» (alexeyten.ya.ru):

А если я вызову какой-то URL, который реально есть на диске, то что? Или пользователь заведёт какой-нибудь URL, совпадающий с тем, что есть? Роутинг нарушится и скачается/выполнится реальный файл?

Lynn «Кофеман» (alexeyten.ya.ru)
23 ноября 2009, 00:53, ответ предназначен bolk (bolknote.ru):

Реально на диске есть статика (картинки, css и т.п.) и php-скрипты. Соответственно, статика отдастся, скрипт выполниться.

Что значит "пользователь заведёт какой-нибудь URL"?

bolk (bolknote.ru)
23 ноября 2009, 10:51, ответ предназначен Lynn «Кофеман» (alexeyten.ya.ru):

Например, на диске лежит файл /lib/common.php, и пользователь решил завести такой же URL в CMS. Кривой пример, но другой придумывать не зачем — это же иллюстрация.

Lynn «Кофеман» (alexeyten.ya.ru)
23 ноября 2009, 13:18, ответ предназначен bolk (bolknote.ru):

Не. Пользователь не может завести такой урл в CMS. Он может заводить только каталоги и «файлы».html. Ну и заливать статику в специально отведённый каталог.

Имена каталогов могут теоретически пересекаться, но пользователь сам себе не враг и зачем ему могут понадобиться каталоги css, js и i, я не знаю :-)

bolk (bolknote.ru)
23 ноября 2009, 23:10, ответ предназначен Lynn «Кофеман» (alexeyten.ya.ru):

Мало ли зачем. Да и непонятно зачем ограничивать пользователя созданием только «.html».

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)