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

    С mod_rewrite есть одна проблема, об которую набиты уже наверное 15 миллионов шишек: он просматривает список правил снова и снова, пока URL удается хоть как-то изменить.

    Очень часто получаеются и бесконечные циклы(например добавление расширения — оно добавляется снова и снова, если специально регэкспом не ограничить), над которыми с непривычки приходится поломать голову. Все надежды на модификатор [L] тщетны — он лишь сразу запускает следующую иттерацию обработки. Да и без бесконечного цикла лишние иттерации скорости работы не добавляют :-)

    Хочу поделится достаточно простым и универсальным средством борьбы с такой особенностью, который обнаружил только-что :-)


    RewriteCond %{ENV:REDIRECT_FINISH} !^$
    RewriteRule ^ - [L]

    #Дальше сколько угодно правил
    RewriteRule ^/?page/([a-z]+)$ read.php?page=$1 [E=FINISH:1,L,QSA]
    RewriteRule ^.*$ 404.php [E=FINISH:1,L]


    Что тут происходит? Как только мы произвели окончательное преобразование URL-а — выставляем переменную окружения FINISH, и на следующей иттерации Апач скопирует старые перменные окружения с префиксом REDIRECT_, увидет установленный REDIRECT_FINISH и закончит работу. Телемаркет :-)

    PS: Встроенный rewrite-engine в nginx лишен этой проблемы с рождения, но это так, к слову ;-)

    UPDATE: В коментах предолжили еще короче, с безусловным обрубанием всего после первой иттерации:

    # Don't loop.
    RewriteCond %{ENV:REDIRECT_STATUS} !^$
    RewriteRule .* — [L]
    Метки:
    Поделиться публикацией
    Похожие публикации
    Комментарии 21
    • +4
      просто вы не умеете их готовить :)
      • 0
        Я то умею, лет 7 уже их пишу время от времени, а вот тем кто впервые сталкивается — бывает сложновато :-)
      • +3
        А у mod_rewrite есть log и debug ;)
        • 0
          Есть, но это не избавляет от необходимости руками писать такие регекспы, которые бы в конце концев приводили бы к «устойчивому урлу», это просто дополнительная, и часто ненужная работа.
        • +9
          # Don't loop.
          RewriteCond %{ENV:REDIRECT_STATUS} !^$
          RewriteRule .* — [L]
          • 0
            Интересное решение, более жесткое, в моём варианте часть правил можно отправить на второй проход, хотя ваш для большинства случаев подойдет :-)
          • 0
            Почему бы просто не писать однозначные правила, которые будут переписывать только то, что нужно?
            • 0
              Потому что для этого нужны дополнительные усилия мозга:

              RewriteRule ^/?page/(.*)$ /page/read.php?page=$1 [E=FINISH:1,L,QSA]

              Как вы это перепишите однозначно?
              • –1
                Мозг для того и нужен, чтобы не использовать конструкции типа (.*)
                • +2
                  Это просто пример. В любом случае, не вижу проблем с (.*) если это действительно нужно.
                  • 0
                    Это я к тому, что (.*) и вызывает зацикливание. Не знаю, когда это может быть действительно нужно, но от такой маски ещё и производительность сильно страдает.
                    • 0
                      Как тогда прикажете FrontController делать?
                      • 0
                        И мне кажется вы переоцениваете сложность этого регэкспа. Готов поспорить, даже на 10'000 запросах в секунду Апач не упрется в этот регэксп :-)
              • 0
                О! Я нашёл это :)))
                Спасибо огромное
                • +1
                  а я в последнее время использую что-то типа

                  RewriteEngine On
                  RewriteBase /

                  RewriteRule ^(.*).png$ $1.png [E=redir:yes,L]
                  RewriteRule ^(.*).gif$ $1.gif [E=redir:yes,L]
                  RewriteRule ^(.*).jpg$ $1.jpg [E=redir:yes,L]
                  RewriteRule ^(.*).css$ $1.css [E=redir:yes,L]
                  RewriteRule ^(.*).js$ $1.js [E=redir:yes,L]

                  RewriteCond %{ENV:REDIRECT_redir} !^yes$
                  RewriteRule .* index.php [L]

                  то есть, все (за исключениями понятно чего) летит в индекс, а дальше уже разгребаю что прилетело в скрипте, мне почему-то так спокойнее
                  • +4
                    шаблон называется FrontController
                    • +8
                      RewriteRule ^(.*).(png|gif|jpg|css|js)$ $1.$2 [E=redir:yes,L]
                      • 0
                        Ну вообще-то файл еще существовать должен.
                        • +2
                          RewriteEngine On
                          RewriteCond   %{REQUEST_FILENAME}       !-d
                          RewriteCond   %{REQUEST_FILENAME}       !-f
                          RewriteRule   ^(.*)                                             index.php?%{QUERY_STRING}
                          
                    • 0
                      О ЧЕЛОВЕЧИЩЕ! Я не знаю как высказать свою благодарность тебе! Я вскипятил себе мозг 10 раз пока не увидел это! Примите все возможные и невозможные способы благодарности от меня.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.