Пользователь
0,3
рейтинг
29 марта 2012 в 11:42

Администрирование → Ещё немного о mod_rewrite из песочницы

Иногда, в создаваемых веб-приложениях необходимо использовать фронт-контроллер, использующий человечески-понятную строку запроса. Как правило, начинающие PHP-программисты делают при этом перенаправление всей строки запроса в нужный скрипт. Например, так:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [L,QSA]


Что эти строчки означают:
1 — включаем mod_rewrite
2 — условие, что все существующие папки не будут обрабатываться регулярным выражением
3 — условие, что все существующие файлы не будут обрабатываться регулярным выражением
4 — регулярное выражение, в нашем случае — при любой строке запроса будет открывать index.php, добавляя её (строку запроса) в $_SERVER['REQUEST_URI'].

Несмотря на то, что данный пример легко найти в любом поисковике за полторы минуты, и он при этом прекрасно работает, хотелось бы отметить один нюанс для начинающих PHP-программистов.
В некоторых случаях при таком использовании mod_rewrite, скрипт будет вызываться неявно несколько раз. И если в коде скрипта до обработки строки запроса присутствуют какие-либо действия с базой данных, или другой функционал, затрагивающий важные данные — он тоже будет выполняться несколько раз, что в некоторых случаях может привести ко крайне нежелательным последствиям, не говоря уже о лишней нагрузке на сервер.

Чем это вызвано?
Когда мы открываем веб-браузер и набираем в нём какой-либо запрос, например http://example.com/pages/example — браузер, отправляет HTTP-запрос к серверу по этому адресу. Но, кроме него, большинство браузеров отправляют ещё один фоновый запрос — на получение favicon.ico. И если данный файл отсутствует, в соответствии с установленными правилами провоцируется новый вызов скрипта, который мы и не видим.
У себя, например, я это заметил лишь когда счётчик посещения страницы по необъяснимым причинам инкрементировался дважды при одном посещении страницы, что для меня поначалу было абсолютно непостижимо.

Как исправить?
Простым костыликом:
RewriteEngine On
#Don't favicon!
RewriteCond %{REQUEST_FILENAME} !^favicon\.ico
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [L,QSA]


Почему костыль?

Потому что, если уж мы и передаём запрос в скрипт целиком — то надо именно его сначала проверить и обработать, а потом уже выполнять какие-либо важные действия. Также использовать такое перенаправление запроса рекомендуется только в самых крайних случаях, и если возможно решить задачу другим способом, то надо решать её другим способом. Более подробно об использовании mod_rewrite написано и в этих замечательных статьях на Хабре:
mod_rewrite — просто о сложном
Практика использования mod_rewrite
Как на самом деле работает mod_rewrite. Пособие для продолжающих

Благодарю за внимание!
iloveYou @stychos
карма
2,0
рейтинг 0,3
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Администрирование

Комментарии (23)

  • +3
    Где вы были зимой?
    Не все знают про этот подводный камень. Но можно было бы добавить различные трики (обрезание/добавление trail slash, убирание www префикса и т.д.)
  • –4
    Статья не о чем. Описано уже много раз. Лучше бы описал про www и без www
    • +3
      Ваш комментарий ни о чем. Возьмите и напишите.
      • –2
        Challenge accepted
        image
        • –2
          • 0
            Challenge failed.
    • 0
      RewriteCond %{HTTP_HOST} ^example.com
      RewriteRule (.*) www.example.com/$1 [R=301,L]

      ?
  • +3
    Вобще-то данный пример зло! На каждую отсутствующую картинку, каждый отсутствующий css/js файил будет запускаться. Если уж пишите как не стоит делать, то пишите до конца. Напишите нормальный .htaccess и расскажите зачем и почему так.
    • +1
      К сожалению, набросав за пол часа свою статью опубликовать её не получилось (даже в черновики) — карма в минусе. Ну и хуй с ней, как говорится, если интересно, возьмите с моего сайта статью, может опубликуете.

      Я делал .htaccess для следующих правил:
      5) Соглашение по определению окончаний в url:
      # site.com/
      # site.com/index -> site.com/
      # site.com -> site.com/
      # site.com/file/ -> site.com/file.html
      # site.com/file -> site.com/file.html
      # site.com/dir/file ->site.com/dir/file.html
      # site.com/dir/file/ -> site.com/dir/file.html
    • 0
      ссылка не опубликовалась: ссылка на хабрапост
    • +3
      Та бля что ж такое почему ссылки не вставляются. nuisho.com/public/habrapost1.txt

      Вобще заебись сделали с этой кармой. Неадекваты слили и теперь нихера нельзя делать.
  • 0
    А как убрать такое?
    http://example.com/index.php/pages/example
    • 0
      RewriteRule ^(.*)$ index.php [QSA,L] вроде (первый результат в гугле)
    • 0
      У меня для свежего CodeIgniter вот такой код работает в htaccess

      RewriteCond %{HTTP:X-Requested-With} !^XMLHttpRequest$
      RewriteCond %{THE_REQUEST} ^[^/]*/index\.php [NC]
      RewriteRule ^index\.php(.*)$ $1 [R=301,NS,L]
      • 0
        Можно спросить?
        А зачем для свежего CodeIgnitier вам понадобилось такое?
  • +1
    Во-первых, я считаю, что лучше явно задать куда можно обращаться напрямую, а то мало ли что:

    RewriteEngine On
    RewriteCond $1 !^(index\.php|images|downloads|css|js|robots\.txt|sitemap\.xml|test.html)
    RewriteRule ^(.*)$ /index.php/$1 [L]


    При этом нужно предусмотреть в index.php работу с несуществующими ссылками, и кидать 404 ошибку не производя ненужных операций (с бд и т.д.)

    Во-вторых, еще нужно добавить проверку на наличие слеша в конце строки, если используете красивую маршрутизацию, как на хабре, иначе поисковики могут трактовать страницы habrahabr.ru/post/140693/ и habrahabr.ru/post/140693 как разные. Смогу вечером написать код проверки для htaccess.

    В-третьих, стоит упомянуть, что если есть возможность, данный конфиг надо впихнуть в конфиг апача (например в конфиг-файл одного из виртуальных хостов), а не в файл .htaccess. Это обусловлено тем, что конфиги apache загружает 1 раз, а этот файл дергает на каждый запрос.
    • 0
      Без работы с БД затруднительно выдавать 404 в большинстве юзкейсов с ЧПУ — ресурсы (в терминах REST) хранятся в БД.
      • 0
        Я наверное не совсем понятно выразился — имелось в виду без ненужных запросов в бд. Если не нашли нужный контроллер для модели MVC, то не стучимся в бд вообще, если нашли контроллер, но нету записи с определенным id, то тоже кидаем 404 без лишних запросов.
    • 0
      Игорь, а как с твоим htaccess обработается запрос с опечаткой? Например, example.com/donloads/. Юзер не увидит красивой 404-ой ошибки в стиле сайта — её выдаст Apache. Так ведь?
      • 0
        Нет, запрос перенаправится в index.php, так как эта папка не указана в исключениях.
        Я юзаю codeigniter, и там стоит на обработке всех 404 ошибок красивая страница в виде сайта, так что покажется она, например как на http://picrand.com/donloads/.

        А вот чтобы при ненахождении файла в папках-исключениях показывать в таком же стиле 404 страницу, надо добавить еще в настройках хоста следующее:
        ErrorDocument 404 /error/error_404/

        Тут /error/error_404/ — путь URL который обработает ошибку и выдаст что нужно, например при запросе к несуществующему файлу http://picrand.com/images/333.gif у меня происходит перенаправление к методу error_404 контроллера error, который формирует соотв. заголовок и выдаёт красивую страничку.

        Если картинки лежат на поддомене, в отдельном месте где-то (в другой папке), тогда сложнее, я пока не разбирался как это сделать, в ближайшие дни буду искать.
        • +1
          IMHO для картинок так лучше не делать) У меня один раз опера показала «пирамидку» из сайта и полчаса пытался понять что не так, пока дошло — убил картинку, вместо неё — «красивая» 404, которая опять же сайт а там опять нет картинки етц.
          • 0
            Да, вы правы, нужна очень легкая 404 страница без извращений для картинок.
  • +1
    Подозревая, что дополнительно тратиться время на проверку существования файлов и возможного отсутствия файла, игнорирую все запросы, соответствующие путям на файлы (имя + расширение файла + аргументы после? для сброса кэша):
    RewriteCond %{REQUEST_URI} !\.[a-zA-Z]{1,4}[\?0-9]*/?$ [NC]
    RewriteRule ^(.*)$ index.php?q=$1 [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]

    В RewriteRule учтена ещё HTTP авторизация.

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