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 раз пока не увидел это! Примите все возможные и невозможные способы благодарности от меня.

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

                    Самое читаемое