Основы и особенности mod_rewrite, примеры использования mod_rewrite, переменные сервера, флаги (RewriteRule Flags), перенаправление, запрет доступа по времени суток или агенту пользователя, запрет доступа по рефереру или при его отсутствии.
Мир Вам Братья и Сёстры! По просьбам трудящихся сегодня я здесь типа обучающая программа, которую зовут Олег, и, сегодня мы с Вами попробуем объяснить, самим себе в первую очередь, основные принципы работы чудо-модуля mod_rewrite дабы иметь отчётливое понимание как работают условия и правила, а не просто тупо их копировать/вставлять. Итак, начнём...
Модуль Apache mod_rewrite является очень мощным, но в то же время сложным, инструментом для манипуляции с URL перенаправлениями/преобразованиями/запретами. С помощью этого чудо-модуля можно выполнять практически любые URL преобразования/манипуляции на стороне сервера.
Обратите внимание, что для некоторых манипуляций с URL не требуется такой мощный и сложный (особо для начинающих) модуль как mod_rewrite. Для простых задач можно использовать mod_alias. Под словом "мощный" имеется ввиду не только широкие возможности по преобразованию URL-ов, но и повышенный расход ресурсов сервера в сравнении с другими модулями.
Регулярные выражения mod_rewrite
mod_rewrite использует Perl Compatible Regular Expression (PCRE - Perl совместимые регулярные выражения). В этой статье, мы не будем подробно описывать использование регулярных выражений имхо этой теме посвящены целые тома книг и в одной статье нельзя охватить данную тематику.
Для большего понимания механизмов PCRE рекомендую запастись какой-то книгой, например "Книга Джеффри Фридла «Регулярные выражения»", или как минимум ознакомьтесь с материалами по ссылкам:
- Регулярные выражения — Википедия
- Секреты регулярных выражений (regular expressions): Часть 1. Диалекты и возможности. Составление регулярных выражений
- perlre - perldoc.perl.org
Главное, что нужно помнить, - это то, что в регулярных выражениях используются специальные символы (метасимволы) и обычные символы (литералы). Основными метасимволами являются [ ] \ / ^ $ . | ? * + ( ) { }. Метасимволы всегда нужно экранировать обратным слэшем "\", - это относится к пробелу ("\ "), а также тому же обратному слэшу ("\\").
Нужно помнить, что mod_rewrite использует оригинальный PCRE (Perl совместимые регулярные выражения), но с некоторыми дополнениями:
- '!Условие' (несоответствие условию)
- '<Условие' (лексически меньше условия)
- '>Условие' (лексически больше условия)
- '=Условие' (лексически равно условию)
- '-d' (является ли каталогом)
- '-f' (является ли обычным файлом)
- '-s' (является ли обычным файлом с ненулевым размером)
- '-l' (является ли символической ссылкой)
- '-F' (проверка существования файла через подзапрос)
- '-U' (проверка существования URL через подзапрос)
Порядок обработки правил mod_rewrite
Порядок обработки правил mod_rewrite является далеко не очевидным. Правила mod_rewrite составляются примерно в таком порядке:
RewriteEngine on RewriteBase / # uncomment this line if web-base dir not root # RewriteBase /you-web-base-dir RewriteCond %{что_сравнивать} с_чем_сравнивать [флаги] RewriteRule исходный_url целевой_url [флаги]
Теперь чуть подробнее:
- RewriteEngine - должна быть одна;
- RewriteBase - может пригодится при использовании в правилах относительных ссылок, но если относительные ссылки относятся к корню каталога, то данная директива может не использоваться, теоретически может использоваться многократно перед каждым из правил;
- RewriteCond - условие, которое должно быть соблюдено перед выполнением правила, условий может быть несколько;
- RewriteRule - собственно само правило, которое выполняется при соблюдении условия.
Порядок размещения правил .htaccess важен потому что механизм преобразований обрабатывает их в специальном порядке. Строчка за строчкой сначала просматриваются RewriteRule директивы и при соответствии URL шаблону (Pattern, исходный_url) конкретного правила проверяются условия (RewriteCond директивы) относящиеся к этому правилу. Условия (RewriteCond) всегда должны быть перед правилами (RewriteRule)! На рис. ниже показан порядок обработки правил mod_rewrite.

Как видно, Текущий URL сначала сравнивается с Шаблон правила и при совпадении с шаблоном проверяет условия, если Текущий URL удовлетворяет условиям, то к нему применяется правило и Преобраз. URL идёт дальше на обработку если не указан флаг [L] (last).
Флаг [L] нужно использовать для каждого правила, разумеется если дальнейшая трансформация URL не требуется.
Далее директива "RewriteEngine on" не будет упоминаться в примерах, - т.е. будем подразумевать, что она уже была добавлена ранее и в дальнейшем дублировать её мы не будем.
Переменные mod_rewrite
В условиях (RewriteCond) и в правилах (RewriteRule) можно использовать переменные сервера.
1. HTTP headers (RqH - Request Header):
- HTTP_USER_AGENT - содержит полную строку заголовка "User-Agent:";
- HTTP_REFERER - адрес с которого пришел пользователь;
- HTTP_COOKIE - доступ к списку COOKIE браузера;
- HTTP_FORWARDED - содержит IP-адрес прокси-сервера или сервера балансировки нагрузки;
- HTTP_HOST - адрес хоста/сервера, который запросил пользователь, например, example.com;
- HTTP_PROXY_CONNECTION - содержит лексему соединения (connection-token) "close" или "Keep-Alive", предназначен для согласования постоянных соединений между клиентом и сервером (аналог заголовка Connection);
- HTTP_ACCEPT - заголовок согласования содержимого, которое поддерживает браузер/клиент пользователя, например "
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" ("тип/подтип", через запятую, где тип – это тип содержимого, а подтип – это уточнение типа.). Если заголовок Accept содержит "Accept: */*" (*/*), то это означает, что клиент готов принять содержимое любого типа.
2. connection & request (соединение & запрос):
- REMOTE_ADDR - IP-адрес клиента;
- REMOTE_HOST - ДНС-имя IP-адреса клиента;
- REMOTE_PORT - номер текущего порта клиента;
- REMOTE_USER - содержит имя авторизированного (средствами сервера) пользователя;
- REMOTE_IDENT - переменная будет установлена только если подключен модуль mod_ident и IdentityCheck установлено в "on", предназначена для работы с Ident протоколом https://ru.wikipedia.org/wiki/Ident;
- REQUEST_METHOD - метод, которым был сделан запрос, GET|GET|HEAD и т.д.;
- SCRIPT_FILENAME - полный путь к запрошенному файлу, например /var/www/public_html/script_name.php;
- PATH_INFO - Содержит предоставленный пользователем путь, который содержится после имени скрипта, но до строки запроса (?). Например, если скрипт был запрошен по URL http://www.example.com/php/path_info.php/some/stuff?foo=bar, то переменная $_SERVER['PATH_INFO'] будет содержать /some/stuff.
- QUERY_STRING - строка GET запроса, если запрошен адрес http://example.com/index.php?var=1&var=2, то QUERY_STRING будет содержать var=1&var=2;AUTH_TYPE - тип аутентификации, если выполнена HTTP-аутентификация, может быть Basic или Digest.
3. server internals (внутренние сервера):
- DOCUMENT_ROOT - полный путь к домашнему каталогу пользователя, например /var/www/public_html/
- SERVER_ADMIN - данные администратора сервера/виртуального_хоста, обычно адрес электронной почты;
- SERVER_NAME - имя сервера, обычно из директивы ServerName;
- SERVER_ADDR - IP-адрес сервера;
- SERVER_PORT - порт сервера;
- SERVER_PROTOCOL - версия используемого протокола, например HTTP/1.0 или HTTP/1.1;
- SERVER_SOFTWARE - название/версия сервера.
4. date and time (системные, дата и время):
- TIME_YEAR - год, 2014
- TIME_MON - месяц, 05
- TIME_DAY - день, 07
- TIME_HOUR - час, 04 (24)
- TIME_MIN - минуты, 38
- TIME_SEC - секунды, 55
- TIME_WDAY - день недели, 3 (среда)
- TIME - в формате год-мес-день-час-мин-сек, например 20140514234534
5. specials (специальные):
- API_VERSION - в формате "20051115:33"
- THE_REQUEST - подробности GET/POST запроса, например "GET /index.html HTTP/1.1"
- REQUEST_URI - относительный УРЛ запроса "/index.html"
- REQUEST_FILENAME - полный локальный путь к файлу или скрипту в файловой системе, соответствующего запроса
- IS_SUBREQ - если выполняется подзапрос, то переменная содержит true, в противном случае false
- HTTPS - on/off, если используется/неиспользуется HTTPS
6. переменные результата выполнения:
- $1 - $1, $2 и т.д. образуются при совпадении (шаблона1.*) (шаблона2.*) из RewriteRule
- %1 - %1, %2 и т.д. образуются при совпадении (шаблона1.*) (шаблона2.*) из RewriteCond
Дополнительные ссылки по HTTP заголовкам и переменным сервера:
- Подробнее о HTTP заголовках можно почитать в спецификации rfc2616 Hypertext Transfer Protocol -- HTTP/1.1
- PHP: $_SERVER - Manual
- Environment Variables in Apache - Apache HTTP Server
Флаги mod_rewrite
Для управления поведением условий (RewriteCond) и правил (RewriteRule) в mod_rewrite используются флаги [флаги].
- [B] (escape backreferences) - заставляет экранировать (кодировать) спец-символы, например взять правило "
RewriteRule ^search/(.*)$ /search.php?term=$1" в котором есть строка поиска, которая может содержать к примеру 'x & y/z' и в результате будет возвращена строка 'search.php?term=x & y/z', что неявляется допустимым УРЛ и будет преобразовано браузером в "search.php?term=x%20&y%2Fz=". С флагом [B] строка будет преобразована в "/search.php?term=x%20%26%20y%2Fz". Для работы этого примера понадобится установить AllowEncodedSlashes в On ибо httpd по-умолчанию не позволяет кодировать слэши в УРЛ - [C] chain - объединить несколько правил в цепочку. Если первое правило цепочки не удовлетворяет условиям, тогда вся цепочка будет проигнорирована
- [CO] cookie - устанавливает cookie в формате
[CO=NAME:VALUE:DOMAIN:lifetime:path:secure:httponly], параметры для secure и httponly устанавливаются как true|false - [DPI] discardpathinfo - отбрасывает PATH_INFO в преобразованной ссылке, полезно использовать в случаях, когда PATH_INFO уже был добавлен в предыдущем преобразовании
- [E] env - установить переменную
[E=VAR:VAL]или удалить её[E=!VAR] - [F] forbidden - возвращает ошибку 403
- [G] gone - возвращает ошибку 410
- [H] handler - принудительно устанавливает обработчик для определённых типов файлов, например правило "
RewriteRule !\. - [H=application/x-httpd-php]" заставит пропустить через PHP все файлы без расширения - [L] last - указывает, что правило является последним и процесс дальнейшего преобразования прекращается
- [N] next - начинает процесс преобразования с первого по порядку правила, используйте этот флаг с осторожностью ибо он может привести к замкнутому циклу (т.н. петля)
- [NC] nocase - отключает проверку регистра символов
- [NE] noescape - mod_rewrite обычно применяет правила экранирования URI к результату преобразования. Спецсимволы (такие как '%', '$', ';', и так далее) будут экранированы их шестнадцатеричными (hexcode) подстановками ('%25', '%24', и '%3B', соответственно). Этот флаг запрещает делать это
- [NS] nosubreq - игнорировать подзапросы, выполнять правило только для настоящих/прямых запросов
- [P] proxy - Apache выполняет подзапрос к указанной странице с использованием программного модуля mod_proxy, при этом клиент об этом подзапросе ничего не узнает. Произойдет ошибка если модуль mod_proxy не подключен
- [PT] passthrough - остановить преобразование и передать полученную новую ссылку дальше
- [QSA] qsappend - добавляет исходные параметры запроса (query string) к замене. Если в подстановку не включаются новые параметры запроса, то исходные параметры запроса будут добавлены автоматически. Если же новые параметры включаются в подстановку, то исходные параметры запроса будут утеряны если не указать флаг QSA
- [R] redirect - возвращает браузеру команду на перенаправление (по-умолчанию код 302 - MOVED TEMPORARY), код редиректа можно указать самостоятельно, например R=301 (код 301 - MOVED PERMANENTLY), но в границах 300-399, в противном случае правило не будет обработано
- [S] skip - пропускает следующее правило, если данное правило сработало. Можно указать количество правил, например: S=2
- [T] type - принудительно устанавливает MIME-тип целевого файла. К примеру, "
RewriteRule \.pl$ - [T=text/plain]", это правило отобразит Perl скрипты в текстовом формате, а значит код скрипта будет выдан в браузер.
Более подробно о флагах читаем в оригинале:
Протоколы разрешённые в mod_rewrite
mod_rewrite будет определять подстановочный УРЛ как внешний если указаны один из протоколов:
- ajp:// - Apache JServ Protocol
- balancer:// - Apache Load Balancer
- ftp:// - File Transfer Protocol
- gopher:// - Gopher (protocol)
- http:// - Hypertext Transfer Protocol
- https:// - Hypertext Transfer Protocol Secure
- ldap:// - Lightweight Directory Access Protocol
- nntp:// - Network News Transfer Protocol
- ldap: - Lightweight Directory Access Protocol
- mailto: - The mailto URI scheme
- news: - News Protocol
.htaccess и порядок размещения правил
В файле .htaccess, теоретически, директивы можно указывать как попало, за исключением директив относящихся к rewrite_module. Тем не менее лучше соблюдать некую последовательность:
- CORE директивы;
- Конфигурация модулей.
Директивы rewrite_module выполняющие перенаправление, а не преобразование, должны быть первыми, в противном же случае после преобразования запроса желаемого перенаправления может не произойти если не учесть произошедшие ранее преобразования.
Примеры mod_rewrite правил
Запрет доступа с помощью mod_rewrite
1. Запрещаем посещать наш веб-сайт в рабочее время:
RewriteCond %{TIME_HOUR}%{TIME_MIN} >2000 [OR] RewriteCond %{TIME_HOUR}%{TIME_MIN} <0700 RewriteRule .* - [ F ] # OR RewriteCond %{TIME_HOUR} >=20 [OR] RewriteCond %{TIME_HOUR} <07 RewriteRule .* - [ F ]
Это правило закроет доступ с 8-и вечера и до 7-и утра. Приведённый выше пример специально для любителей копи/пасты содержит преднамеренную ошибку в синтаксисе, HTTP 500 (ошибка сервера), которая повлечёт запись в логе ошибок RewriteRule: bad flag delimiters
. Переводится, как плохой разделитель флагов, - вместо [ F ] нужно использовать [F], т.е. избегать пробелов и прочих разделителей, кроме запятой!
2. Наглухо запрещаем боту WBSearchBot трогать наш сайт:
RewriteCond %{USER_AGENT} WBSearchBot RewriteRule .* - [F] # Ещё как вариант, вместо ошибки 403 (FORBIDDEN), отдаём ошибку 404 (NOT FOUND) RewriteCond %{USER_AGENT} WBSearchBot RewriteRule .* - [R=404]
Хотя, немного подправив правило, можно т.с. перевести стрелы на кого-то ещё, так сказать натравить бота на другой сайт, мол ...ты что, офонарел!? Иди тренируйся, вон, на нём...
:)) (из Х/Ф Операция «Ы» и другие приключения Шурика):
RewriteRule .* http://kremlin.ru [NC,R=303,L]
WBSearchBot (Mozilla/5.0 (compatible; WBSearchBot/1.1; +http://www.warebay.com/bot.html)) довольно агрессивный бот и от него можно смело избавляться.
3. Запрещаем POST запросы для старых протоколов:
# BLOCK POST REQUEST FOR OLD HTTP PROTOCOL... RewriteCond %{THE_REQUEST} ^POST [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.|www1\.)?example.com [NC,OR] RewriteCond %{THE_REQUEST} POST(.*)HTTP/(0\.9|1\.0) [NC,OR] RewriteCond %{HTTP_USER_AGENT} ^$ RewriteRule .* - [L,F]
В предыдущем примере код ответа 303 See Other
указан не случайно, - дело в том, что если метод перенаправления (обычно GET) отличается от метода запроса (например POST), то в случае возврата кодов ответа 301-302 вместо автоматического редиректа в браузер будет выдана страница со ссылкой для ручного перехода по ней! В случае же с ответом 303 See Other
перенаправление выполняется автоматически методом GET независимо от метода запроса.
4. Hotlink protection - запрещаем отображение наших изображений с других сайтов:
# # HOTLINK PROTECT... # http://www.htaccesstools.com/hotlink-protection/ RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.|www1\.)?remoteshaman.com [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?google.com [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yandex.ru [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?subscribe.ru [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?feedburner.com [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?mail.ru [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?poisk.ru [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?rambler.ru [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?nigma.ru [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?ask.com [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?qip.ru [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?ukr.net [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?conduit.com [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?tut.by [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?bing.com [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?webalta.ru [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yahoo.com [NC] RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?conduit.com [NC] RewriteRule \.(jpg|jpeg|png|gif) https://dl.dropboxusercontent.com/u/52572427/ \ images/wrs-hotlink-deny.jpg [R,L]
- HTTP — Википедия
- Internet Explorer использует запросы GET HTTP/1.0 вместо запросов GET HTTP/1.1 для подключения к веб-узла в Windows XP
- Hurl.it - Make HTTP requests
mod_rewrite перенаправления
1. Перенаправляем запрос к сайту http://example.com/ (без www префикса) на http://www.example.com/ (с www префиксом):
# REDIRECT ON WWW... RewriteCond %{HTTP_HOST} ^example\.com$ [NC] RewriteRule ^(.*)$ http://www.example.com/$1 [R=301,L] # OR # REDIRECT TO WWW. OR BACK... # Set protossl environment and check %{HTTPS} # if = on, then set "s" in protossl RewriteRule ^ - [E=protossl] RewriteCond %{HTTPS} on RewriteRule ^ - [E=protossl:s] # To redirect all users to access the site WITH the 'www.' prefix, # (http://example.com/... will be redirected to http://www.example.com/...) # uncomment the following: RewriteCond %{HTTP_HOST} . RewriteCond %{HTTP_HOST} !^www\. [NC] RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] # OR # REDIRECT FOR /DOCS Redirect permanent /docs http://docs.example.com
2. Перенаправляем наши RSS/ATOM ленты на FeedBurner
# REDIRECT blog RSS/ATOM to FeedBurner... # if HTTP_USER_AGENT not FeedBurner or FeedValidator RewriteCond %{HTTP_USER_AGENT} !^.*(FeedBurner|FeedValidator) [NC] RewriteCond %{QUERY_STRING} ^option=com_content&view=featured&Itemid=([0-9])+&format=feed [NC] RewriteRule index.php http://feeds.feedburner.com/remote-shaman-blog? [NC,R=302,L] # # REDIRECT forum RSS/ATOM to FeedBurner... # if HTTP_USER_AGENT not FeedBurner or FeedValidator RewriteCond %{HTTP_USER_AGENT} !^.*(FeedBurner|FeedValidator) [NC] # forum/topics/mode-topics?format=feed # forum/topics/mode-latest?format=feed # forum/topics/posts?format=feed # forum/recent?format=feed RewriteCond %{QUERY_STRING} ^format=feed$ [NC] RewriteRule forum/([a-zA-Z]*/)?([a-zA-Z])+ http://feeds.feedburner.com/remote-shaman-forum? [L,NC,R=302]
Обратите внимание, что в конце каждой ссылки в правилах (RewriteRule) стоит символ "?", - он требуется для того, чтобы в конец ссылки, по которой будет перенаправлен запрос, не добавлялись параметры QUERY_STRING! если не указать символ "?", то в итоге перенаправление будет по адресу http://feeds.feedburner.com/remote-shaman-blog?option=com_content&view=featured&Itemid=... и http://feeds.feedburner.com/remote-shaman-forum?format=feed соответственно.
Итоги
mod_rewrite является мощным инструментом и с его помощью можно творить чудеса, - это факт! В этой статье не так много примеров, но их валом в сети, а вот материала о том, что к чем и почему в стиле как для полных идиотов практически не встретить. Возможно папизже позднее на эту страницу будут добавлены ещё примеры использования mod_rewrite.
В этой статье я постарался максимально доходчиво изложить (надеюсь мне это удалось) базовые принципы работы чудо-модуля mod_rewrite, в стиле как для полных идиотов (в хорошем смысле слова;), имхо на собственном опыте знаю как оно иногда тяжко осваивать что-то с ноля и когда маны и принципы этого что-то описаны общими туманными фразами и глубоко техническими терминами.
При написании данного материала использовались только оригинальные маны с сайта httpd.apache.org (никакой копи/пасты). Если я что-то пропустил аль где-то протупил, тогда обязательно напишите об этом в комментариях.
Ссылки по теме mod_rewrite
- Apache mod_rewrite Introduction - Apache HTTP Server
- mod_rewrite - Apache HTTP Server
- Using mod_rewrite to control access - Apache HTTP Server
- Список кодов состояния HTTP — Википедия

