Пример работы с mod_rewrite

Многие веб-разработчики наслышаны про человекопонятные УРЛы (ЧПУ), но до сих пор теряются в том, как это реализуется.  Несложно найти в инете примеры mod_rewrite директив, но не всегда понятно до конца как это работает. В данном посте я постараюсь расставить всё по своим местам.

Итак, начнём с того, что реализуется это за счёт модуля Apache под названием mod_rewrite. Для подключения его необходимо создать в корневой папке сайта (например, http://ctepeo.net/) файл с расширением .htaccess (без имени), который в свою очередь и будет сообщать серверу, что для данной папки и вложенных папок правила определяются не общими настройками сервера, а директивами, указанными в нём.

Создали? Продолжим. Откроем данный файл в удобном для Вас текстовом редакторе и начнём добавлять директивы.

RewriteEngine on
RewriteBase /

Первая строчка указывает серверу, что необходимо подключить модуль rewrite (он же mod_rewrite).  Вторая строчка указывает путь, относительно которого будут действовать правила переадресаций, то есть, если Вам необходимо реврайтить пути не из текущей директории, а например, из папки test, находящейся в корневой директории сайта, то RewriteBase будет /test/.

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
ErrorDocument 404 /index.php

Эти строчки указывают нам на то, что при физическом наличии файла – открывать именно его, в противном случае вызывается ошибка 404 «Документ не найден». Именно в этом месте и начинается трюк. Вместо обычной статичной html странички мы указываем серверу, что нужно показать index.php.

В index.php легко узнать за какой страницей пришёл пользователь (какой УРЛ он вводил), делается это примерно так:

$xurl = explode(‘/’,$_SERVER['REQUEST_URI']);

Эта строчка разбивает строку запроса (Например, http://ctepeo.net/test/myarticle/testme.html) на массив, получая элементы такие как: [0] – ctepeo.net, [1] – test, [2] – myarticle, [3] testme.html. Далее мы зная запрос можем вызывать необходимый нам модуль (Например, сделав запрос в базе по FakeURL (testme) и показав именно страницу testme, хотя физически файла /test/myarticle/testme.html не существует) или отобразить сообщение, что такого файла действительно нету.

Внимание! Для корректной индексации поисковиками в начале php-кода необходимо принудительно указывать header 200 OK. Делается это так:

<?php

Header(«HTTP/1.0 200 OK»);

?>

В некоторых случаях необходимо делать исключения, например, следующая строчка

RewriteRule ^admin/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/?$ index.php?action=admin [NC,L]

перенаправит все запросы вида /admin/testme/test123/ на index.php с GET action=admin.

Mod_rewrite так же может использоваться и для защиты файлов или директорий от внешних запросов.

RewriteRule ^.config.inc.php$ – [F]

Данная директива будет возвращать ошибку 403 Forbidden при попытке вызывать файл config.inc.php

Подводя итог, хочется сказать, что mod_rewrite штука весьма несложная и в то же время очень мощная, поэтому стоит уделить ей час-два и навсегда избавится от таких насущных проблем, как дружественные УРЛ (friendly URLs).

Комментарии (15) »


  RSS комментариев этой записи.


Просто подписка на комментарии

  1. а что значит [NC,L] в примере исключения?

    Comment от Стас — 03.01.2009 @ 05:42

  2. [NC,L] – флаги. NC означает, что регистр не имеет значения. Т.е. http://www.example.com/catalog/ и http://www.example.com/cAtALoG/ будут вести к одному и тому же, а именно, в соответствии с нашим правилом, к http://www.example.com/index.php?page=catalog. L означает, что если это правило подходит под наш URL, то следующие правила не применять, т.е. остановиться.

    (c) http://bestblog.name/2008/11/modrewrite.html

    Comment от ctepeo — 03.01.2009 @ 05:52

  3. По поводу защиты файлов
    Лучше уже использовать

    order deny,allow
    deny from all
    allow from 127.0.0.1

    - возможностей больше :)
    А вообще скрывать требуется чаще всего каталоги, с данными, нежели какой-то отдельный файл

    deny from all

    Comment от Николаев Алексей — 06.03.2009 @ 11:55

  4. Теги мне повырезало :(

    order allow,deny
    deny from all

    и соответственно

    Order Deny,Allow
    Deny from All

    Comment от Николаев Алексей — 06.03.2009 @ 11:56

  5. ппц, и с пробелами повырезало :( оно что, не может при посте htmlspesialchars сделать :(

    Comment от Николаев Алексей — 06.03.2009 @ 11:58

  6. В любом случае, идея понятна :) Вы сказали, что «лучше» – хотелось бы уточнить чем лучше? Производительность? В принципе реврайт правилом можно и директории прятать под 403 :)

    И ещё: в данном методе не учтена одна штука: в заголовке страницы (header) необходимо передавать 200ый код («ОК»), чтобы поисковики не считали все страницы сайта одной 404ой, то есть статья апдейтед

    Comment от ctepeo — 06.03.2009 @ 12:00

  7. Почему лучше – директива Files проверяет обращение к файлу, а RewriteRule проверяет обращение по адресу.
    Пример
    сайт : http://tanya1.com
    в корне имеем: test.php
    в .htaccess имеем: RewriteRule ^test.php$ – [F,L]
    Вроде все класно – имя файла описано от начала и до конца, «засады» ждать неоткуда.
    Обращаемся на: http://tanya1.com/test.php и получаем как и следовало ожидать 403 нет доступа
    Тем не менее ломается достаточно просто.
    Набираем: http://tanya1.com/test.php%5C10 и файл test.php успешно сообщает нам «Hello world» :)
    В случае с
    Files test.php
    Deny from all
    Files
    такой «финт ушами» не проходит.
    Подобных извращений с url в какерских целях достаточно много и если уж запрещать доступ к файлу, то делать это директивами для работы именно с файлами, реврайт тут немного не к месту.

    ЗЫ: А вообще тема важная и нужная. ПлусадЫн. Прочитай я подобную статью когда делал первые шаги, много нервов сохранил бы. Единственное я бы в статью добавил ссылку на приличный мануал по регулярным выражениям. Т.к. они используются в правилах, а статья рассчитана на новичков – им было бы полезно.

    Comment от Николаев Алексей — 07.03.2009 @ 20:43

  8. Относительно поста выше – хотел знакомому показать, не получилось. Начал копать – оказалось то, что я написал, во втором апаче уже не работает :) . Но тем не менее на домашней сборке отработала бага.

    Comment от Николаев Алексей — 09.03.2009 @ 11:24

  9. Николаев Алексей, проверьте в конфиге апача разрешение на использование .htaccess, думаю, что проблема именно в этом, потому что данные директивы работают у меня лично на apache 2.2

    Comment от ctepeo — 09.03.2009 @ 11:31

  10. Только что на втором апаче попробовал обойти правило RewriteRule ^test.php$ – [F,L]
    Получилось, просто адрес немного другой будет
    набираем http://tanya1.com/test.php/
    т.е. со слешем на конце и правило перестает работать – скрипт запускается

    Comment от Николаев Алексей — 09.03.2009 @ 11:34

  11. Николаев Алексей, под регулярное выражение ^test.php$ попадает только test.php без всяких вариаций, думаю, я накосячил и стоило писать что-то вроде ^test.php?$, хотя не уверен (не проверял). В любом случае будет полезно почитать про регулярные выражения. Например, вот тут: http://ru.wikipedia.org/wiki/Регулярные_выражения

    Comment от ctepeo — 09.03.2009 @ 11:42

  12. А что надо прописать чтобы при заходе в http://test.com/folder/ выдавало ошибку 403 а при заходе явно на http://test.com/folder/index.php обрабатывало корректно ???

    Comment от dementiy — 07.11.2009 @ 20:51

  13. Возник вопрос. у себя на сайте в целях проверки решил сделать дружественный URL к странице http://altermuz.ru/view_mus.php?id=45

    код :
    RewriteRule ^poltora.html /view_mus.php?id=45

    теперь если в браузере ввести
    http://www.altermuz.ru/poltora.html

    действительно откроется эта же страница, но не наоборот (((( то есть ссылка не подменяется налету . с чем это может быть связано ???

    думаю что может потомучто в php коте ссылка у меня генерируется следующим образом:

    где %s это данные из БД. но если проблема в этом то как тогдабыть ???
    помогите плиз.

    Comment от Amadey — 12.02.2010 @ 23:06

  14. dementy,
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-l

    AllowOverride None
    Order Deny,Allow
    Deny from all

    Comment от ctepeo — 13.02.2010 @ 16:03

  15. Amadey, так или иначе файл-обработчик будет доступен. Мод_рерайт помогает создать «дружественные» УРЛы. Вариант решения: храните в базе УРЛы + обработчики, например:

    /poltora.html | view.php
    /distemper.html | view.php
    /news.html | news.php

    а в .htaccess пропишите перенаправление всех запросов на один файл, который и будет искать в базе по запросу нужный обработчик, примерно вот так:

    Options -Indexes
    RewriteEngine On
    RewriteRule ^$ /engine.php [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-l
    RewriteRule (.*)$ /engine.php [QSA]

    а в файле /engine.php можно получить УРЛ через $_REQUEST, выбрать нужный нам обработчик(напримре, view.php), в нём, зная куда шёл пользователь (1.5кг или дистемпер, например), выбрать нужную информацию и показать её.

    Если возникнут какие-либо вопросы, пишите :)

    Comment от ctepeo — 13.02.2010 @ 16:11

Оставить комментарий


Сделано ctepeo на базе wordpress