Что вам нужно знать об.htaccess. mod_rewrite: архивный ЧПУ-адрес


Для перенаправления, настройки чпу на веб сайтах использующих и , и вставки ключевых слов в url`ы статей, все директивы прописанные в данной статье прописываются в файле .htaccess который обычно находится в корне вашего сайта, хотя в некоторых cms он есть практически в каждой в папке но это уже совсем другая история….

Ниже приведены 5 примеров использования данного модуля:

1) Переделываем «product.php?id=12» в «product-12.html»

Это простое перенаправление, в котором расширение.php спрятано из адресной строки браузера и динамический УРЛ (с знаком вопроса «?») преобразован в статический адрес

RewriteEngine on
RewriteRule ^product-(+)\.html$ product.php?id=$1

2) Переделываем «product.php?id=12» в «product/ipod-nano/12.html»

Эксперты SEO всегда предлагают показывать главное ключевое слово в УРЛе. В пример Вы можете видеть название продукта в УРЛе.

RewriteEngine on
RewriteRule ^product/(+)/(+)\.html$ product.php?id=$2

3) Перенаправление адресов без www URL на адреса с www — редирект

Если Вы введете yahoo.com в браузере, Вас перенаправит на www.yahoo.com. Для проделывания такой же операции на Вашем сайте добавьте следующий код в файл.htaccess:

RewriteEngine On
RewriteCond %{HTTP_HOST} ^optimaxwebsolutions\.com$
RewriteRule (.*) http://www.optimaxwebsolutions.com/$1

УРЛ сайта, конечно же, поменяйте на свой. Для чего делать такой редирект? Чтобы избежать дублирования сайта поисковиками с www и без www.

4) Переделываем «yoursite.com/user.php?username=xyz» в «yoursite.com/xyz»

В файл.htaccess добавляем следующие строки:

RewriteEngine On
RewriteRule ^(+)$ user.php?username=$1
RewriteRule ^(+)/$ user.php?username=$1

5) Перенаправление домена на новый поддомен или папку.

Допустим, Вы сделали редизайн на сайте и обновленный сайт находится в папке “new” в корне сайта. То есть новый сайт доступен по адресу “test.com/new”. Перенос файлов из одного места в другое может быть довольно трудоемким процесом, так что просто добавьте следующие строки в файл.htaccess и разместите его в корневой папке:

RewriteEngine On
RewriteCond %{HTTP_HOST} ^test\.com$ RewriteCond %{HTTP_HOST} ^www\.test\.com$
RewriteCond %{REQUEST_URI} !^/new/
RewriteRule (.*) /new/$1

Теперь при обращение к «www.test.com» все файлы будут браться из “test.com/new”

mod_rewrite: статический ЧПУ-адрес с одной переменной

Итак, в этой статье я говорил, что сайты на PHP и MySQL имеют адреса следующего формата:

Как правило, такие адреса называют динамическими. Вот мы сейчас и займёмся преобразованием динамических адресов в ЧПУ.

Допустим, нужно преобразовать из lis.php?id=3 в bols3.hi . Регулярное выражение будет иметь следующий формат:

RewriteRule ^НАЗВАНИЕ СТРАНИЦЫ(+)\.РАСШИРЕНИЕ$ ИМЯ НАСТОЯЩЕГО АДРЕСА.php?ПЕРЕМЕННАЯ=$НУМЕРАЦИЯ

То есть в нашем случае получаем следующее:

RewriteRule ^bols(+)\.hi$ lis.php?id=$1

Теперь вместо адреса lis.php?id=90 (где 90 — id) мы можем спокойно обращаться к bols90.hi .

mod_rewrite: статический ЧПУ-адрес с множеством переменных

Рассмотрим такую ситуацию, когда нужно преобразовать адрес с множеством переменных. Например, из lis.php?id=345&cat=3 в bols345-3.hi . Ситуация похожая, но сейчас используется две GET-переменные. В качестве разделителя используется тире. Получаем следующее выражение:

RewriteRule ^bols(+)-(+)\.hi$ lis.php?id=$1&cat=$2

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

mod_rewrite: архивный ЧПУ-адрес

Многие архивы на сайте имеют адрес archive.php?year=2003&month=10 . Мы же преобразуем его в archive/2003/10/ . Получаем следующую строку:

RewriteRule ^archive/(+)/(+)\$ archive.php?year=$1&month=$2

mod_rewrite: ЧПУ-адрес для тегов

Сейчас теги присутствуют почти на каждом блоге и сайте. Попробуем изменить адрес для тега winter — posts.php?tag=winter в posts/tags/winter/ . Имеем следующее выражение:

RewriteRule ^posts/tags/(+)\$ posts.php?tag=$1

Кстати, для индексации страниц с динамическими адресами поисковые системы применяют отдельный алгоритм. Я не знаю чем он отличает от обычного, но ЧПУ-преобразования , опять же, помогают указать роботу, что нужно индексировать наш адрес, как обычную статическую страницу.


Примеры записей в htaccess: Индексный файл , Редирект с сохранением рейтинга страницы, Склеивание www и http , Создание ЧПУ или ЧеловекуПонятныхУрлов, Редирект всех файлов папки на один файл, Защита от хотлинков , Определение кодировки и многое другое!

Указываем индексный файл (который первым открывает при обращении к сайту)

DirectoryIndex index.php index.html index.shtml
Можно указать один или несколько файлов

Редирект htaccess php

Redirect / http://www.newsite.ru/
Глобальный редирект(первый /) ВСЁ пересылаем на новый адрес http://www.newsite.ru/

Redirect /katalog http://www.newsite.ru/newkatalog
Все обращения к katalog переадресуем на домен newsite.ru в раздел newkatalog

Редирект с сохранением рейтинга страницы

Redirect 301 /old/old.php http://www.yoursite.ru/new.php
old - пример пути старой страницы. new - пример пути новой страницы.

Редирект пользователя с конкретным ip

SetEnvIf REMOTE_ADDR 192.100.220.1 REDIR="redir"
RewriteCond %{REDIR} redir
RewriteRule ^/$ /out.html
Конкретного пользователя (надоел он или нужно его переслать на внутренний раздел) с данным ip пересылает на страницу /out.html

Склеивание www и http htaccess php

Уже многим SEO (продвижение и оптимизация) известно, что для Поисковых Систем адреса с/без www - это разные адреса и доступность сайта с www отрицательно влияет на индексацию и ранжирование. Поэтому зачастую SEO просят разработчиков склеить сайт без www (чтобы при обращении к сайту с www происходила пересылка на без www, т.е. только http)

RewriteEngine On
RewriteCond %{HTTP_HOST} ^www.yoursite\.ru$
RewriteRule ^(.*)$ http://yoursite.ru/$1
Теперь даже если Вы наберёте в адресной строке www.yoursite.ru , то сервер перешлёт Вас на http://yoursite.ru

Создание ЧПУ или Человеко-Понятных-Урлов htaccess php

RewriteEngine on
RewriteRule ^article/([^/\.]+)/?$ article.php?id=$1 [L]
После добавления данной строки в .htaccess будут доступны два адреса для документа. Например: и www.yoursite.ru/article/1

RewriteEngine on
RewriteRule cat/(.*)/(.*)/$ /art.php?$1=$2
В результате www.yoursite.ru/art.php?type=123 превращается в www.yoursite.ru/cat/type/123/ :

Вот ещё частные варианты:

RewriteEngine on
RewriteRule katalog-saitov[/]*$ article.php?id=$1 [L]
Статья с технически адресом www.yoursite.ru/article.php?id=1 теперь будет доступна со своим понятным человеку названием www.yoursite.ru/katalog-saitov .

Редирект всех файлов папки на один файл

Например вы больше не нуждаетесь в разделе сайта articles и хотите перенаправить все запросы к папке /articles на один файл /non-articles.php. Поможет код ниже

RewriteRule ^articles(.*)$ /non-articles.php

Защита от хотлинков htaccess php

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

RewriteEngine On
#В строке с?yoursite\.ru/ меняете данную конструкцию на УРЛ Вашего сайта
RewriteCond %{HTTP_REFERER} !^http://(.+\.)?yoursite\.ru/
RewriteCond %{HTTP_REFERER} !^$
#Меняем /images/exit.jpg на другое изображение. Можно неприличное
RewriteRule .*\.(jpe?g|gif|bmp|png)$ /images/exit.jpg [L]

Определение кодировки htaccess php

Дополнительные варианты самых популярных кодировок: UTF-8, Windows-1251, KOI8-R . В примерах рассмотрим самую распространённую UTF-8
AddDefaultCharset UTF8 # кодировка файлов, в которой по умолчанию отдаёт документы
AddCharset UTF8 .html # Пример: обрабатывает в данной кодировке html
AddCharset UTF8 * # Пример: обрабатывает в данной кодировке Все файлы

# Обработка в данной кодировке определённого файла

AddCharset UTF8 .html

CharsetDisable On # Отменяем перекодировку Сервером загруженных файлов
CharsetDefault UTF8 # Кодировка, передаваемая Сервером Браузеру по умолчанию
CharsetSourceEnc UTF8 # Принудительная Перекодировка ВСЕХ загруженных на сервер файлов

Создание своих страниц ошибок

Если Вам хочется видеть после неправильной ссылки другую страницу, сделанную, например, самим собой, то указываем в.htaccess следующий код (ну и соответственно делаем там свои страницы):

# ошибка сервера, неверный запрос
ErrorDocument 400 /error/badrequest.html

# вход запрещён
ErrorDocument 403 /error/forbid.html

# самая распространённая - страница не найдена
ErrorDocument 404 /error/notfound.html

# внутренняя ошибка сервера
ErrorDocument 500 /error/serverr.html

Запрещаем доступ

Скорее всего Вы столкнётесь с тем, что часть файлов и директорий надо будет закрыть от общего доступа.

Закрываем от всех

Deny from all

Закрываем конкретный файл от всех


deny from all

Разрешаем доступ только с одного ip

Order deny,allow
deny from all
allow from 192.111.37.125

Запрещаем доступ с конкретных ip


order allow,deny
allow from all
deny from 192.111.35.122
deny from 192.111.37.171

Убираем из Урла (URL) расширение файла

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.php
# php можно заменить другим расширением. Например: html, htm, shtml, asp

Запрещаем отображать содержимое директории, если нет индексного файла

Вы скорее всего хоть раз видели текст Index of и список файлов. Так происходит в том случае, когда в директории нет индексного файла (например index.php), а система предлагает выбрать файл для дальнейшего открытия. Минус этого заключается в том, что случайный пользователь может увидеть список и содержание всех файлов директории.
Options -Indexes

Основы и особенности 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 совместимые регулярные выражения ). В этой статье, мы не будем подробно описывать использование регулярных выражений имхо этой теме посвящены целые тома книг и в одной статье нельзя охватить данную тематику.

  • Секреты регулярных выражений (regular expressions): Часть 1. Диалекты и возможности. Составление регулярных выражений

Главное, что нужно помнить, - это то, что в регулярных выражениях используются специальные символы (метасимволы ) и обычные символы (литералы ). Основными метасимволами являются \ / ^ $ . | ? * + () { } . Метасимволы всегда нужно экранировать обратным слэшем "\", - это относится к пробелу ("\ "), а также тому же обратному слэшу ("\\").

Нужно помнить, что 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 не требуется.

Переменные 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 заголовках можно почитать в спецификации rfc2616 Hypertext Transfer Protocol -- HTTP/1.1

Флаги 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 - объединить несколько правил в цепочку. Если первое правило цепочки не удовлетворяет условиям, тогда вся цепочка будет проигнорирована
  • cookie - устанавливает cookie в формате , параметры для secure и httponly устанавливаются как true|false
  • discardpathinfo - отбрасывает PATH_INFO в преобразованной ссылке, полезно использовать в случаях, когда PATH_INFO уже был добавлен в предыдущем преобразовании
  • [E] env - установить переменную или удалить её
  • [F] forbidden - возвращает ошибку 403
  • [G] gone - возвращает ошибку 410
  • [H] handler - принудительно устанавливает обработчик для определённых типов файлов, например правило " RewriteRule !\. - " заставит пропустить через PHP все файлы без расширения
  • [L] last - указывает, что правило является последним и процесс дальнейшего преобразования прекращается
  • [N] next - начинает процесс преобразования с первого по порядку правила, используйте этот флаг с осторожностью ибо он может привести к замкнутому циклу (т.н. петля)
  • nocase - отключает проверку регистра символов
  • noescape - mod_rewrite обычно применяет правила экранирования URI к результату преобразования. Спецсимволы (такие как "%", "$", ";", и так далее) будут экранированы их шестнадцатеричными (hexcode ) подстановками ("%25", "%24", и "%3B", соответственно). Этот флаг запрещает делать это
  • nosubreq - игнорировать подзапросы, выполнять правило только для настоящих/прямых запросов
  • [P] proxy - Apache выполняет подзапрос к указанной странице с использованием программного модуля mod_proxy, при этом клиент об этом подзапросе ничего не узнает. Произойдет ошибка если модуль mod_proxy не подключен
  • passthrough - остановить преобразование и передать полученную новую ссылку дальше
  • 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$ - ", это правило отобразит 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. Тем не менее лучше соблюдать некую последовательность:

  1. CORE директивы;
  2. Конфигурация модулей.

Директивы rewrite_module выполняющие перенаправление, а не преобразование, должны быть первыми, в противном же случае после преобразования запроса желаемого перенаправления может не произойти если не учесть произошедшие ранее преобразования.

Примеры mod_rewrite правил

Запрет доступа с помощью mod_rewrite

RewriteCond %{TIME_HOUR}%{TIME_MIN} >2000 RewriteCond %{TIME_HOUR}%{TIME_MIN} <0700 RewriteRule .* - [ F ] # OR RewriteCond %{TIME_HOUR} >=20 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 .* -

Хотя, немного подправив правило, можно т.с. перевести стрелы на кого-то ещё, так сказать натравить бота на другой сайт, мол...ты что, офонарел!? Иди тренируйся, вон, на нём... :)) (из Х/Ф Операция «Ы» и другие приключения Шурика):

RewriteRule .* http://kremlin.ru

WBSearchBot (Mozilla/5.0 (compatible; WBSearchBot/1.1; +http://www.warebay.com/bot.html) ) довольно агрессивный бот и от него можно смело избавляться.

# BLOCK POST REQUEST FOR OLD HTTP PROTOCOL... RewriteCond %{THE_REQUEST} ^POST RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.|www1\.)?example.com RewriteCond %{THE_REQUEST} POST(.*)HTTP/(0 \.9|1 \.0) RewriteCond %{HTTP_USER_AGENT} ^$ RewriteRule .* -

В предыдущем примере код ответа 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\.)?сайт RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?google.com RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yandex.ru RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?subscribe.ru RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?feedburner.com RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?mail.ru RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?poisk.ru RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?rambler.ru RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?nigma.ru RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?ask.com RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?qip.ru RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?ukr.net RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?conduit.com RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?tut.by RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?bing.com RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?webalta.ru RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yahoo.com RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?conduit.com RewriteRule \.(jpg|jpeg|png|gif) https://dl.dropboxusercontent.com/u/52572427 / \ images/wrs-hotlink-deny .jpg

  • RewriteRule ^ - RewriteCond %{HTTPS} on RewriteRule ^ - # 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\. RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} # OR # REDIRECT FOR /DOCS Redirect permanent /docs http://docs.example.com

    2. Перенаправляем наши RSS/ATOM ленты на FeedBurner

    # REDIRECT blog RSS/ATOM to FeedBurner... RewriteCond %{HTTP_USER_AGENT} !^.*(FeedBurner|FeedValidator) RewriteCond %{QUERY_STRING} ^option=com_content&view=featured&Itemid=()+&format=feed RewriteRule index.php http://feeds.feedburner.com/remote-shaman-blog? # # REDIRECT forum RSS/ATOM to FeedBurner... # if HTTP_USER_AGENT not FeedBurner or FeedValidator RewriteCond %{HTTP_USER_AGENT} !^.*(FeedBurner|FeedValidator) # 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$ RewriteRule forum/(*/)?()+ http://feeds.feedburner.com/remote-shaman-forum?

    Обратите внимание, что в конце каждой ссылки в правилах (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 mod_rewrite — это модуль веб сервера Apache , использующийся для преобразования URL адресов. Под преобразованием следует понимать фактически любые действия с URL. Это очень мощное и в то же время гибкое средство, имеющее очень широкие возможности. Модуль позволяет производить практически любые типы преобразований. С помощью mod_rewrite можно настраивать редиректы, изменять URL адреса, блокировать доступ и т.д. Он поддерживает неограниченное количество правил преобразования, регулярные выражения , обратные связи с группированными частями шаблона, разные источники информации для преобразований (переменные сервера, HTTP заголовки, время и т.д.). За счет такого набора возможностей, достигается высокая функциональность и гибкость. По умолчанию этот модуль выключен, для того что бы его включить, в.htaccess необходимо добавить следующие директивы:

RewriteEngine On
RewriteBase /

RewriteEngine On — директива включает модуль.
RewriteBase — указывает путь от корня сайта до файла.htaccess. Если.htaccess лежит в корне, то указывать этот параметр нужно как в примере, если во внутреннем каталоге, то указываем путь к этому каталогу, например /images.

Принцип работы модуля mod_rewrite

Работа модуля основана на наборе правил и условий, согласно которым производится преобразование. При получении запроса, Apache передает в mod_rewrite путь к файлу начиная от того места, где находится файл.htaccess, остальная часть пути обрезается. Если поступил запрос http://some-url.com/cat/cat2/file.html, а.htaccess лежит в корне, то в mod_rewrite попадет cat/cat2/file.html (без слеша в начале). Если.htaccess лежите в директории /cat, то в mod_rewrite попадет cat2/file.html. Далее mod_rewrite анализирует правила в.htaccess и действует согласно этих правил. Стоит знать, что mod_rewrite работает не со ссылками и не с URL адресами, а с обычными строками. То есть адрес, который нужно преобразовать, передается mod_rewrite как обычная строка, и эту строку можно преобразовать как угодно. Для построения правил используются две директивы, RewriteCond и RewriteRule (более детально эти директивы описаны ниже).​
RewriteCond — в этой директиве определяются условия, при которых сработает правило преобразования RewriteRule. Если условие в RewriteCond выполнено, выполняем правило в RewriteRule. Таких условий перед правилом RewriteRule может быть неограниченное количество. RewriteCond не является обязательной директивой для создания правила преобразования и может отсутствовать.
RewriteRule — здесь уже указывается само правило для преобразования, которое для конкретного преобразования должно быть единственным.
Пример, как это выглядит в.htaccess:

RewriteCond %{REQUEST_URI} !.(ico|css|js|txt)$
RewriteCond %{REQUEST_FILENAME} !^/admin

Несмотря на то, что директива RewriteCond стоит выше, чем правило RewriteRule, mod_rewrite сначала проверяет строку на соответствие с шаблоном в RewriteRule, и если строка совпадает с шаблоном, он смотрит на указанные выше условия в RewriteCond. Если условия тоже совпадают, происходит преобразование согласно правилу RewriteRule. Рассмотрим подробней синтаксис и предназначение директив RewriteCond и RewriteRule.

RewriteCond

Как уже писалось выше, в этой директиве указываются условия, при которых правило в директиве RewriteRule будет выполнено. Эта директива выглядит так:

RewriteCond [строка_для_сравнения] [условие] [флаг]
RewriteCond %{REQUEST_URI} !.(ico|css|js|txt)$

В этом примере правило условие будет выполнено, если запрос пользователя не содержит расширение ​ico,css,js или txt.
Строка для сравнения — кроме обычного текста может содержать регулярное выражение, обратные RewriteCond и RewriteRule связи и переменные сервера. На практике здесь используются переменные сервера и иногда регулярные выражения.
Условие — собственно это то, с чем сравнивается строка для сравнения. Может содержать текст, регулярные выражения и специальные символы:

  • "-d" — проверяет правильность пути (его существование) и является ли этот путь, путем к каталогу.
  • "-f" — проверяет правильность пути (его существование) и является ли этот путь, путем к обычному файлу.
  • "-s" — то ж, что и -f, но дополнительно проверяет, что размер файла больше 0 (ноля).
  • "-l" — проверяет правильность пути (его существование) и является ли этот путь символической ссылкой.
  • "-F" — проверяет через внутренний подзапрос, является ли сравниваемая строка реально существующим файлом, при этом используются все существующие списки контроля доступа сервера. Это негативно сказывается на производительности, стоит использовать осторожно.
  • "-U" — проверяет через внутренний подзапрос, является ли сравниваемая строка реально URL адресом, при этом используются все существующие списки контроля доступа сервера. Это негативно сказывается на производительности, стоит использовать осторожно.
Дополнительно, перед условием, допускается использование логических символов:
  • "!" — инвертирование значения, указывает на то, что сравниваемая строка должна не соответствовать шаблону условия.
  • "<" — лексически меньше. Например символ "a" лексически меньше символа "b", "a" < "b".
  • ">" — лексически больше.
  • "=" — равенство, используется по умолчанию.

Флаг — необязательный параметр, в котором указываются дополнительные опции (через запятую, если их несколько). Указывается в конце правила в квадратных скобках .

  • — регистронезависимый, то есть регистр (A-Z или a-z) в строке для сравнения или в условии не имеет значения.
  • — логическое ИЛИ. Используется, когда перед директивой RewriteRule находится несколько директив RewriteCond и правило в RewriteRule должно быть выполнено при совпадении одного из RewriteCond.​ Если флаг OR не указан, RewriteRule сработает только при соответствии всех директив RewriteCond.

RewriteRule

​В RewriteRule указывается правило для преобразования, то, как мы хотим изменить URL. По факту эта директива также содержит условие, при совпадении которого, будет произведено преобразование. Это шаблон, с которым сверяется полученная mod_rewrite строка. Стоит отметить, что если ничего подставлять не нужно, а такие случаи иногда происходят, в новом значении необходимо указать прочерк "-" . Схематически правило RewriteRule выглядит следующим образом:

RewriteRule [шаблон] [новое_значение] [флаг]
RewriteRule ^(.*)$ /index.php [L]

Шаблон — то, с чем будет сравниваться исходная строка. Исходная строка необязательно является той, которую запросил пользователь. Она могла быть ранее изменена другими правилами RewriteRule. Может содержать обычный текст, регулярные выражение и обратные RewriteCond и RewriteRule связи. Исходная строка, это путь от файла.htaccess до файла, доменного имени там нет.
Новое значение — это значение, на которое будет изменена исходная строка после преобразования. Может содержать обычный текст, регулярные выражение, обратные RewriteCond и RewriteRule связи и переменные сервера.
Флаг — ​необязательный параметр, в котором указываются дополнительные опции, (через запятую, если их несколько). Указывается в конце правила в квадратных скобках .

  • — редирект. code — это код ответа браузеру, по умолчанию используется 302 (временно перемещен), поэтому для постоянного редиректа используйте код 301.
  • [F] — запрет доступа к URL, Forbidden . Сервер возвращает браузеру ошибку с кодом 403.
  • [G] — возвращает ошибку 410, URL не существует.
  • [P] — Apache выполняет подзапрос к указанному адресу с использование другого модуля Apache mod_proxy.
  • [L] — последнее правило. Говорит о том, что на этом месте следует остановить преобразование URL.
  • [N] — процесс преобразований будет запущен опять, начиная с самого первого правила. Будет использована уже модифицированная ранее строка.
  • [C] — связь со следующим правилом, создается цепочка правил. Если правило не соответствует, все последующие правила в цепочке пропускаются.
  • — срабатывают правила только для запросов, подзапросы игнорируются.
  • [T] — принудительно указать MIME-тип файла.
  • — не учитывать регистр символов.
  • — дополнять строку запроса, а не заменять ее. Флаг стоит использовать при работе с GET параметрами в переменной %{QUERY_STRING}, что бы их не терять. Если это флаг не указан, данные в %{QUERY_STRING} будут полностью заменены параметрами из RewriteRule. Если флаг указан, новые параметры будут добавлены в начало %{QUERY_STRING}.
  • — запрещает преобразование специальных символов в их hex эквиваленты.
  • — останавливает преобразование и передает строку дальше для обработки другими директивами (Alias, ScriptAlias, Redirect и т.д.).
  • [S] — пропустить следующее правило. Есть возможность указать несколько правил в формате S=N, где N это количество правил.
  • — установить переменную окружения, где VAR это имя переменной, а VAL ее значение.Значение может быть обратной RewriteCond и RewriteRule связью или текстом.
  • — установить cookie в браузер. NAME — имя куки, VAL — значение, domain — имя домена, lifetime — время жизни (опционально), path — путь, для которого эта кука валидна, по умолчанию равна "/", secure — если установлено 1 или true, куки будут действительны только при https (безопасном) соединении, httponly — если установлено 1 или true, куки будут доступны для JavaScript.

Обратные связи RewriteCond и RewriteRule

Обратные связи, это возможность использования группы символов (заключенные в скобки "()" ) для их последующей подстановки. Например в скобках можно указать определенное регулярное выражение и таким образом охватить большое количество адресов.
$N — позволяет использовать группу символов из шаблона директивы RewriteRule.
%N — позволяет использовать группу символов из шаблона директивы RewriteCond.
Вместо символ "N" в обоих случаях используется число от 1 до 9.
На практике это выглядит следующим образом. Рассмотрим простой пример.
Есть адрес с определенной вложенность, http://some-url.com/cat1/cat2/cat3/cat4/page.html. Есть желание сделать страницу http://some-url.com/cat1/cat2/cat3/cat4/page.html доступной по адресу http://some-url.com/page.html, но кроме page.html, там есть куча других файлов с расширением html, которые также должны быть доступны по новому адресу. Это решается очень просто:

RewriteRule ^cat1/cat2/cat3/cat4/(.*).html$ $1.html

Теперь, при обращении к по адресу http://some-url.com/page.html, будет отображаться информация с адреса http://some-url.com/cat1/cat2/cat3/cat4/page.html и так со всеми адресами вида http://some-url.com/*.html. Точно также, с использованием "%N", можно подставлять группы символов из шаблона для RewriteCond. В данном примере, вместо $1 подставляется группа символов в скобках из шаблона.

Переменные сервера

​Переменные сервера могут содержать много полезной информации, которую можно и нужно использовать для построения правил. Ниже приведен список этих переменных:
HTTP_USER_AGENT — дает информацию о браузере и ОС пользователя. При посещении сайта пользователь, передается User Agent, по факту это обозначает ПО, с помощью которого производится доступ к сайту.
HTTP_REFERER — адрес страницы, с которой был осуществлен переход на сайт.
HTTP_COOKIE — список cookie, которые передает браузер.
HTTP_FORWARDED — адрес страницы, с который был переход. Большой разницы с HTTP_REFERER я не заметил.
HTTP_HOST — адрес сервера (сайта).
HTTP_ACCEPT — это пожелания клиента, по типу документа, который он хочет получить. На деле это выглядит так, браузер отправляет на сервер в http заголовке типы файлов, которые он хочет получить (обычно это относится к изображениям и другим медиа файлам), то есть сообщает, какой тип файла он может обработать.
REMOTE_ADDR — IP адрес посетителя.
REMOTE_HOST — адрес (хост) пользователя, который отдается командой "host" по IP адресу.
REMOTE_IDENT — имя пользователя в формате имя.хост.
REMOTE_USER — то же самое что и REMOTE_IDENT, но не содержит хост пользователя.
​REQUEST_METHOD — тип запроса к сайту (GET, POST, HEAD).
SCRIPT_FILENAME — полный путь к запрошенному файлу или адресу.
PATH_INFO — данные, которые передавались в скрипт.
QUERY_STRING — строка, переданная как запрос в CGI скрипт, GET параметры.
AUTH_TYPE — тип идентификации пользователя.
DOCUMENT_ROOT — путь к корневой директории сервера.
SERVER_ADMIN — email администратора сервера.
SERVER_NAME — адрес (имя) сервера, отдаваемый командой host.
SERVER_ADDR — IP вашего сайта.
SERVER_PORT — порт, га котором работает Apache.
SERVER_PROTOCOL — версия http протокола.
SERVER_SOFTWARE — используемая версия Apache.
TIME_YEAR, TIME_MON, TIME_DAY, TIME_HOUR, TIME_MIN, TIME_SEC, TIME_WDAY, TIME — время.
API_VERSION —версия API модуля Apache.
THE_REQUEST — строка содержит весь http запрос, отправленный браузером на сервер (GET /index.html HTTP/1.1). Здесь не включены дополнительные заголовки.
REQUEST_URI — адрес, запрошенный в http заголовке.
REQUEST_FILENAME — полный путь к запрошенному файлу, по факту содержит те же данные, что и SCRIPT_FILENAME.
IS_SUBREQ — проверка на подзапрос. Если да — ответ true, если нет — ответ false.
Список переменных вашего сервера, вы можете легко узнать поместив в корень сайта php файл с кодом:

Набрав адрес этого файла в браузере, внизу страницы вы получите информацию о переменных сервера.

Эта статья выросла из идеи продвинутого обучения наших сотрудников технической поддержки работе с mod_rewrite. Практика показала, что после изучения имеющихся в большом количестве учебников на русском языке саппортам хорошо дается решение шаблонных задач, но вот самостоятельное составление правил происходит методом проб и большого количества ошибок. Проблема заключается в том, что для хорошего понимания работы mod_rewrite требуется изучение оригинальной англоязычной документации, после чего - либо дополнительные разъяснения, либо часы экспериментов с RewriteLog.

В статье изложен механизм работы mod_rewrite. Понимание принципов его работы позволяет четко осознавать действие каждой директивы и ясно представлять себе, что происходит в тот или иной момент внутри mod_rewrite при обработке директив.

Я предполагаю, что читатель уже знаком с тем, что такое mod_rewrite, и не буду описывать его основы, которые легко найти в интернете. Также нужно отметить, что в статье освещается работа mod_rewrite при использовании его директив в файле.htaccess. Отличия при работе в контексте изложены в конце статьи.

Итак, вы изучили mod_rewrite, составили несколько RewriteRule и успели столкнуться с бесконечными перенаправлениями, со случаем, когда правило почему-то не ловит ваш запрос, а также с непредсказуемой работой группы правил, когда последующее правило неожиданно изменяет запрос, кропотливо подготовленный правилами предыдущими.

Почему так происходит?

С чем работает RewriteRule

Первому RewriteRule передается путь от того места, где находится.htaccess, до запрошенного файла. Эта строка никогда не начинается со "/". Последующим RewriteRule передается результат предыдущих преобразований.

Чтобы досконально понять, как работает RewriteRule, необходимо сначала определить, с чем он работает . Рассмотрим, как Apache получает строку, которая изначально передается на обработку RewriteRule в.htaccess.

Когда только начинаешь работать с mod_rewrite, логично предполагаешь, что он работает со ссылками. Однако в случае с использованием mod_rewrite в.htaccess это не так. На самом деле в RewriteRule передается не ссылка, а путь до запрошенного файла .

Из-за внутренней архитектуры Apache в тот момент, когда в действие вступает.htaccess, mod_rewrite может оперировать только с путем до файла, который должен быть обработан. Это связано с тем, что до передачи в mod_rewrite запрос уже могли изменить другие модули (например, mod_alias), и итоговый путь до файла на сайте уже может не совпадать с исходной ссылкой. Если бы mod_rewrite работал с исходной ссылкой, он бы нарушал действие модулей, которые изменили запрос до него.

Поэтому в mod_rewrite передается абсолютный путь до файла, который должен быть обработан. Также mod_rewrite знает путь до.htaccess, в котором размещены правила RewriteRule. Чтобы сделать из пути до файла что-то похожее на ссылку, с которой планирует работать разработчик сайта, mod_rewrite отрезает от абсолютного пути часть до файла.htaccess.

Так вот, именно этот путь, от которого отрезан путь до.htaccess, передается в первый RewriteRule. Например:

  • Запрос: http://example.com/templates/silver/images/logo.gif
  • DocumentRoot: /var/www/example.com
  • Путь до файла: /var/www/example.com/templates/silver/images/logo.gif
  • .htaccess находится в: /var/www/example.com/templates/.htaccess
  • В первый RewriteRule будет передано: silver/images/logo.gif
  • Обратите внимание: «templates/» тоже отрезалось.

Путь до.htaccess отрезается вместе со слешем. Из этого есть следствие: строка, которая изначально передается на обработку RewriteRule никогда не начинается со "/".

Важно запомнить, что не делает RewriteRule. Она не обрабатывает имя сайта, аргументы, которые переданы в скрипт, да и ссылку обрабатывает не всю, если.htaccess размещен не в корне сайта. Всем этим занимается RewriteCond, которого кратко коснемся чуть позже. Итак:

# работать не будет - правило начинается со /
RewriteRule ^/index.php$ /my-index.php

# работать не будет - название сайта не анализируется RewriteRule
RewriteRule ^example.com/.* http://www.example.com

# Будет работать только если.htaccess находится там же, где находится папка templates,
# например, в корне сайта. То есть, если.htaccess находится в templates/.htaccess , правило
# работать НЕ БУДЕТ, потому что mod_rewrite отрежет путь до.htaccess и на вход RewriteRule
# строка попадет уже без "templates/"
RewriteRule ^templates/common/yandex-money.gif$ templates/shared/yad.gif


В начале использования mod_rewrite я рекомендую работать с ним только в.htaccess в корне сайта. Это несколько упростит контроль за его работой.

С чем работает RewriteRule, мы разобрались. Теперь посмотрим, как он работает.

Как работает RewriteRule

RewriteRule просто преобразовывает строку в соответствии с регулярными выражениями, и все. RewriteRule работает со строкой, а не со ссылкой или путем до файла.

Как мы выяснили выше, на вход RewriteRule попадает путь от.htaccess до запрошенного файла. Удобнее всего теперь абстрагироваться от путей и ссылок и рассматривать то, с чем работает RewriteRule, как обычную строку . Эта строка передается от RewriteRule к RewriteRule, видоизменяясь, если какое-то из RewriteRule сработало.

В общем виде, если исключить сложности с использованием флагов (некоторые из которых мы рассмотрим ниже) и сложности с составлением регулярных выражений (которых мы почти не будем касаться в этой статье), RewriteRule работает ОЧЕНЬ просто.

  1. Взяли строку.
  2. Сравнили с регулярным выражением в первом аргументе.
  3. Если есть совпадение - заменили всю строку на значение второго аргумента.
  4. Передали строку следующему RewriteRule.
Вот, в общем, и все. Чтобы наглядно проиллюстрировать, что RewriteRule работает именно со строкой , рассмотрим следующий фантастический пример:
# Запрос: http://mysite.com/info.html
# В первый RewriteRule попадет "info.html"

# Преобразовываем запрос в произвольную строку.
RewriteRule ^info.html$ "I saw a turtle in the hole. And it was dancing rock-n-roll. And it was smiling. All in all, it was a very funny doll."

# "info.html" -> "I saw a turtle..."

# "I saw a turtle..." -> "https://example.com/information/index.html"

# Заменяем имя сайта!
RewriteRule ^(.*)example.com(.*)$ $1example.org$2

# "https://example.com/information/index.html" -> "https://example.org/information/index.html"

# Заменяем протокол!
RewriteRule ^https:(.*)$ ftp:$1

# "https://example.org/information/index.html" -> "ftp://example.org/information/index.html"

# "ftp://example.org/information/index.html" -> "ftp://example.org/information/main.php"


Как видите, RewriteRule все равно, с чем работать - она просто преобразовывает строку в соответствии с заданными ей аргументами. Если хотите, можете в строке хранить любые массивы данных, при желании, настойчивости и хорошем знании регулярных выражений можете хоть крестики-нолики на RewriteRule написать.

Здесь нужно сделать замечание: хоть RewriteRule и работает с чистой строкой, она все-таки ориентирована на работу со ссылками. Поэтому она будет по-особому реагировать на строки, начинающиеся на «https://» или аналоги (запомнит, что мы хотели сделать внешний редирект) и на символ "?" (посчитает следующие символы аргументами, которые нужно будет подставить к запросу). Однако сейчас нас это не интересует - важно понять, что в RewriteRule нет никакой магии - она просто берет строку и изменяет ее так, как вы ей сказали. Внешние редиректы и аргументы мы рассмотрим позже в статье, там тоже есть, о чем поговорить.

После того как все преобразования произведены и выполнено последнее RewriteRule, вступает в силу RewriteBase.

Для чего нужен RewriteBase

Если получившийся после преобразований запрос является относительным и отличается от исходного, RewriteBase добавит себя к нему слева. Нужно обязательно указывать RewriteBase в.htaccess. Его значение - путь от корня сайта до.htaccess.
RewriteBase выполняется только после всех RewriteRule, а не между ними.

Мы уже говорили выше о том, что в mod_rewrite, работающий в.htaccess, попадает абсолютный путь до запрошенного файла. Чтобы передать его в RewriteRule, mod_rewrite отрезает путь до.htaccess. Потом правила RewriteRule одно за одним последовательно изменяют запрос. И вот после того, как запрос изменен, Apache должен восстановить абсолютный путь до файла, который он должен в итоге обработать. RewriteBase фактически является хаком, который помогает восстановить исходный путь до файла.

RewriteBase выполняется после всех преобразований. Это значит, что он не будет изменять запрос между RewriteRule, а вступит в силу только когда все RewriteRule отработают.

После всех преобразований RewriteBase смотрит, относительный получился в итоге путь или абсолютный. В контексте Apache имеется в виду относительный или абсолютный путь, отсчитывая от корня сайта:

  • images/logo.gif - относительный.
  • /images/logo.gif - абсолютный (в начале слеш).
  • http://example.com/images/logo.gif - самый абсолютный из всех.
Если путь абсолютный, RewriteBase ничего не делает. А если относительный - RewriteBase дописывает себя слева. Это работает как для внутренних, так и для внешних редиректов:
# .htaccess находится в /images/
# RewriteBase указан /images/
RewriteBase /images/

# Запрос http://example.com/images/logo.gif
# На вход RewriteRule попадает "logo.gif"
RewriteRule ^logo.gif$ logo-orange.gif
# После RewriteRule: "logo.gif" -> "logo-orange.gif"
# После RewriteBase: "logo-orange.gif" -> "/images/logo-orange.gif"

# Запрос http://example.com/images/header.png
# На вход RewriteRule попадает "header.png"
RewriteRule ^header.png$ /templates/rebranding/header.png
# После RewriteRule: "header.png" -> "/templates/rebranding/header.png"
# После RewriteBase: ничего не меняется, так итоговый результат преобразований начинается со "/".

# Запрос http://example.com/images/director.tiff
# На вход RewriteRule попадает "director.tiff"
# Используем внешний относительный редирект
RewriteRule ^director.tiff$ staff/manager/director.tiff
# После RewriteRule: "director.tiff" -> "staff/manager/director.tiff"
# + mod_rewrite запомнил, что будет внешний редирект
# После RewriteBase: "staff/manager/director.tiff" -> "/images/staff/manager/director.tiff"
# mod_rewrite вспомнил про внешний редирект:
# "/images/staff/manager/director.tiff" -> http://example.com/images/staff/manager/director.tiff


Обычно после некоторого знакомства с mod_rewrite складывается следующая привычка: 1) в каждый.htaccess добавлять «RewriteBase /», 2) все перенаправления начинать со слеша: «RewriteRule news.php /index.php?act=news». Это помогает избавиться от артефактов работы RewriteBase, но так делать неправильно. Теперь, когда нам известно, что делает RewriteBase, можно сформулировать следующие корректные правила:
  1. RewriteBase должен совпадать с путем от корня сайта до.htaccess.
  2. Начинать перенаправления со "/" нужно только тогда, когда необходимо указать абсолютный путь от корня сайта до файла.


Что будет, если не указать RewriteBase? По умолчанию Apache делает его равным абсолютному пути на файловой системе до.htaccess (например, /var/www/example.com/templates/). Некорректность такого предположения Apache проявляется на внешних относительных редиректах:
# Запрос http://example.com/index.php
# DocumentRoot: /var/www/example.com/
# .htaccess находится в корне сайта, и в нем НЕ УКАЗАН RewriteBase.
# Поэтому по умолчанию RewriteBase равен абсолютному пути до.htaccess: /var/www/example.com/

# На входе RewriteRule - "index.php"
RewriteRule ^index.php main.php [R]
# На выходе: "index.php" -> "main.php"
# mod_rewrite запомнил, что нужен внешний редирект

# Закончились RewriteRule
# mod_rewrite все равно выполняет RewriteBase, так как у него есть значение по умолчанию.
# Получается: "main.php" -> "/var/www/example.com/main.php"

# Здесь mod_rewrite вспоминает, что был внешний редирект:
# "/var/www/example.com/main.php" -> http://example.com/var/www/example.com/main.php

# Получилось совсем не то, что имели в виду.


Итак, запрос прошел через все RewriteRule, после чего к нему, в случае необходимости, добавился RewriteBase. Должен ли теперь Apache отдать файл, на который показывает результирующий путь? Нет. Теперь получившийся запрос будет обрабатываться еще раз.

Как работает mod_rewrite. Флаг [L]

mod_rewrite запускает обработку запроса снова и снова, до тех пор, пока он не перестанет меняться. И флаг [L] не может это остановить.

При составлении более-менее сложных конфигураций mod_rewrite важно понимать, что изменение запроса не заканчивается на последнем RewriteRule . После того, как сработало последнее правило RewriteRule и был добавлен RewriteBase, mod_rewrite смотрит, изменился запрос или нет. Если запрос изменился, его обработка начинается заново с начала.htaccess.

Apache поступает так, потому что в процессе изменения запроса он мог быть перенаправлен в другую директорию. В ней может быть собственный.htaccess, который не участвовал в предыдущей обработке запроса. В этом же новом.htaccess могут быть правила, которые влияют на обработку запроса - как правила mod_rewrite, так и правила других модулей. Чтобы корректно обработать эту ситуацию, Apache должен запустить весь цикл обработки заново.

— Постойте, но ведь есть флаг [L] , который останавливает обработку запроса mod_rewrite"ом!

Не совсем так. Флаг [L] останавливает текущую итерацию обработки запроса. Однако если запрос был изменен теми RewriteRule, которые все-таки успели отработать, Apache запустит цикл обработки запроса заново с первого RewriteRule.

#

RewriteRule ^a.html$ b.html [L]
RewriteRule ^b.html$ a.html [L]


Пример выше приведет к бесконечному циклу перенаправлений и к «Internal Server Error» в итоге. В этом примере бесконечный цикл очевиден, однако в более сложных конфигурациях может потребоваться покопаться в правилах, чтобы определить, какие запросы зацикливаются между собой.
  1. Когда используется внешний редирект - или . В случае внешнего редиректа дальнейшая обработка запроса нежелательна (см. ниже про флаг [R]), и ее лучше остановить.
  2. Когда в.htaccess есть зацикливание, от которого не избавиться, и обработку запроса mod_rewrite"ом нужно принудительно прекратить. В этом случае используется специальная конструкция - см. в конце статьи советы на эту тему.
А вот приведенный ниже пример зацикливаться не будет. Попробуйте определить, почему, и какой в итоге файл будет отдан Apache"м.
# Запрос: http://example.com/a.html
# Начало.htaccess

RewriteBase /
RewriteRule ^a.html$ b.html
RewriteRule ^b.html$ a.html

# Конец.htaccess


Отгадка: В результате выполнения всех RewriteRule запрос меняется таким образом, что конечный результат равен исходному . Apache видит это и не запускает повторную обработку запроса . Будет возвращен файл a.html .

Как работает mod_rewrite. Флаг [R]

Флаг [R] не останавливает обработку запроса, возвращая сразу внешний редирект. Вместо этого он запоминает необходимость внешнего редиректа, и обработка запроса продолжается следующими RewriteRule. Рекомендуется всегда использовать с флагом [L].

Флаг [R] сообщает Apache, что нужно выполнить не внутренний, а внешний редирект. Чем отличается внешний редирект от внутреннего? Внутренний редирект просто изменяет путь до файла, который будет отдан пользователю, при этом пользователь считает, что получает тот файл, который он изначально запросил. При внешнем же редиректе Apache вместо содержимого файла возвращает пользователю статус ответа 301 или 302 и сообщает ссылку, по которой браузер должен обратиться для получения файла.

Казалось бы, при обработке флага [R] Apache должен сразу прекратить обработку RewriteRule и вернуть пользователю внешний редирект. Однако давайте вспомним фантастический пример из раздела «Как работает RewriteRule». В нем мы сначала указали флаг [R], обозначив необходимость внешнего редиректа, после чего продолжили изменять ссылку следующими RewriteRule.

Именно так и работает Apache при указании внешнего редиректа. Он просто «помечает» себе, что после выполнения всех правил необходимо вернуть статус 302 (по умолчанию), но при этом продолжает выполнение всех RewriteRule дальше по списку. Мы можем и дальше изменять запрос как нам нужно, единственное, что не получится - сделать редирект обратно внутренним.

Тем не менее, вряд ли вы хотите после отдачи внешнего редиректа каким-либо образом изменять его. Поэтому рекомендуется при употреблении флага [R] указывать его совместно с [L]:

# BlackJack переехал на красивое имя
RewriteRule ^bj/(.*) blackjack/$1

Вместо использования флага [R] можно указывать просто внешнюю ссылку. В этом случае Apache сам догадается, что необходимо сделать внешний редирект. Здесь, как и с в случае с явным указанием флага [R], рекомендуется использовать флаг [L].
  • Если внешний редирект ведет на тот же сайт, лучше использовать флаг [R] без указания полной ссылки (иными словами, использовать относительный внешний редирект). Это сделает правило независимым от имени сайта.
  • Если же внешний редирект ведет на другой сайт, иначе, как указав полную внешнюю ссылку, это сделать не получится.

Как работает mod_rewrite. Указание параметров запроса и флаг

Изменение параметров запроса в RewriteRule не изменяет строку, с которой работает следующий RewriteRule. Однако при изменении параметров изменяется переменная %{QUERY_STRING}, с которой может работать RewriteCond.

Используемая терминология: «параметры» - параметры запроса, «аргументы» - аргументы RewriteRule.

С помощью RewriteRule можно изменять не только путь до файла, который будет обрабатываться, но и параметры запроса GET, которые будут ему передаваться. Это часто используется для передачи обработки ЧПУ в общий скрипт-обработчик, например:

RewriteBase /

# Запрос: http://example.com/news/2010/07/12/grand-opening.html
# На входе: "news/2010/07/12/grand-opening.html"

# После RewriteRule: "news/2010/07/12/grand-opening.html" -> "index.php"
# %{QUERY_STRING}: "" -> "act=news&what=2010/07/12/grand-opening.html"


В момент, когда правило RewriteRule встречает вопросительный знак во втором аргументе, оно понимает, что происходит изменение параметров в запросе. В результате происходит следующее:
  1. RewriteRule заменяет строку, с которой оно работает, на часть второго аргумента до вопросительного знака . Обратите внимание, что новые параметры запроса не попадают в строку, с которой будут работать последующие правила RewriteRule.
  2. Часть второго аргумента после вопросительного знака попадает в переменную %{QUERY_STRING}. Если был указан флаг , параметры запроса будут добавлены в начало %{QUERY_STRING}. Если флаг указан не был, %{QUERY_STRING} полностью заменится параметрами запроса из RewriteRule.
Еще пара примеров:
RewriteBase /

#

RewriteRule ^news/(.*)$ index.php?act=news&what=$1
# После преобразования: "news/2010/" -> "index.php"
# Значение %{QUERY_STRING}: "page=2" -> "act=news&what=2010/"


Скорее всего, правило выше работает неправильно, так как теряется аргумент page. Исправим это:
RewriteBase /

# Запрос: http://example.com/news/2010/?page=2
# На входе RewriteRule: "news/2010/"
RewriteRule ^news/(.*)$ index.php?act=news&what=$1
# После преобразования: "news/2010/" -> "index.php"
# Значение %{QUERY_STRING}: "page=2" -> "act=news&what=2010/&page=2"


Мы добавили только флаг , и правило стало работать корректно.

Важно понимать, что изменение параметров запроса изменяет %{QUERY_STRING} , который может использоваться в дальнейшем в RewriteCond. Это нужно учитывать при составлении последующих правил, проверяющих аргументы.

— Конечно, изменяется, ведь запрос уходит на повторную обработку Apache"м!

Нет, %{QUERY_STRING} изменяется сразу же . Доказательство приводить не буду - про параметры и так уже написано больше, чем интересно читать:)

Что же делать, чтобы проверить в RewriteCond именно те параметры запроса, которые передал пользователь, а не модифицированные RewriteRule"ами? Смотрите советы в конце статьи.

RewriteCond и производительность

Сначала проверяется совпадение запроса с RewriteRule, а уже потом - дополнительные условия RewriteCond.

Пару слов стоит сказать о том, в каком порядке mod_rewrite выполняет директивы. Так как в.htaccess сначала идут RewriteCond, а потом RewriteRule, кажется, что mod_rewrite сначала проверяет все условия, а потом приступает к выполнению RewriteRule.

На самом деле все происходит наоборот. Сначала mod_rewrite проверяет, подходит ли текущее значение запроса под регулярное выражение RewriteRule, а уже потом будет проверять все условия, перечисленные в RewriteCond.

Так что если у вас в RewriteRule регулярное выражение на две страницы и вы, задумавшись о производительности, решили ограничить выполнение этого правила дополнительными RewriteCond, знайте — ничего не получится. В этом случае лучше использовать флаги RewriteRule [C] или [S] , чтобы пропустить более сложное правило, если более простые проверки не сработали.

Переменные и флаги RewriteCond, остальные флаги RewriteRule и прочее

Читайте документацию.

Мы познакомились с принципами работы RewriteRule, RewriteBase, флагов [L], [R] и , а также разобрали механизм обработки запросов внутри mod_rewrite. Из незатронутого остались: другие флаги RewriteRule, директивы RewriteCond и RewriteMap.

К счастью, эти директивы и флаги не таят в себе каких-либо загадок и работают именно так, как описано в большинстве учебников. Для их понимания достаточно почитать официальную документацию. В первую очередь рекомендую изучить список переменных, которые можно проверять в RewriteCond — %{QUERY_STING}, %{THE_REQUEST}, %{REMOTE_ADDR}, %{HTTP_HOST}, %{HTTP:header} и т. д.)

Разница в работе mod_rewrite в контексте.htaccess и в контексте VirtualHost

В контексте mod_rewrite работает с точностью до наоборот.

Как я говорил в начале статьи, все описанное выше касается применения mod_rewrite в контексте.htaccess. Если же mod_rewrite используется в , он будет работать по-другому:
  • В в RewriteRule попадает весь путь запроса, начиная от первого слеша, заканчивая началом параметров GET: «http://example.com/some/news/category/post.html?comments_page=3» -> "/news/category/post.html". Эта строка всегда начинается со /.
  • Второй аргумент RewriteRule также необходимо начинать со /, иначе будет «Bad Request».
  • RewriteBase не имеет смысла.
  • Проход правил происходит только один раз. Флаг [L] действительно заканчивает обработку всех правил, описанных в , без каких-либо последующих итераций.
Здесь собраны советы, которые можно было бы привести по ходу статьи, но которые были исключены из основного текста для краткости изложения материала.

Составление регулярных выражений

Старайтесь составлять регулярные выражения так, чтобы они наиболее узко определяли именно те запросы, которые вы хотите модифицировать - чтобы правила RewriteRule случайно не сработали для другого запроса. Например:
# Начинайте все регулярные выражения с "^" (признак начала строки)
# и заканчивайте "$" (признак конца строки):
RewriteRule ^news.php$ index.php
# Даже если в этом нет необходимости - для универсальности и лучшего понимания конфигурации:
RewriteRule ^news/(.*)$ index.php

# Если под маску должны попадать только цифры - укажите это явно.
# Если какие-то цифры постоянны, укажите их явно.
# Если в оставшейся части запроса не могут присутствовать слеши, ограничьте их присутствие.
# Не забывайте экранировать "." (точки).
# Следующее правило нацелено на запросы вида http://example.com/news/2009/07/28/b-effect.html
RewriteRule ^news/20{2}/{2}/{2}/[^/]+\.html index.php


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

Изменение внешних редиректов

Несмотря на то, что mod_rewrite позволяет изменять с помощью RewriteRule даже внешние редиректы, вплоть до протокола, я крайне не рекомендую делать это. В статье пример с изменением внешних редиректов используется только чтобы отвязаться от таких понятий как «ссылки» и «файлы» и более явно показать, что RewriteRule работает с простой строкой.

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

Как остановить бесконечный цикл

Иногда логика перенаправлений на сайте такова, что без специальных действий mod_rewrite воспринимает их как бесконечный цикл перенаправлений. Возьмем следующий пример.

На сайте была страница /info.html. Специалист по SEO решил, что поисковые системы будут лучше индексировать эту страницу, если она будет называться /information.html и попросил сделать внешний редирект с info.html на information.html. Однако разработчик сайта по каким-то своим соображениям не может просто переименовать info.html в information.html и сделать редирект - ему нужно, чтобы данные обязательно отдавались непосредственно из файла info.html. Он пишет следующее правило:

# сделать внешний редирект
RewriteRule ^info.html information.html
# но по запросу /information.html все равно отдать info.html
RewriteRule ^information.html info.html

… и сталкивается с бесконечным циклом. Каждый запрос /information.html получает внешний редирект снова на /information.html.

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

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


RewriteRule ^information.html$ info.html


Обратите внимание, что к имени переменной mod_rewrite добавляет "REDIRECT_".

Второй способ - проверить в THE_REQUEST, что именно было запрошено пользователем:

# Внешний редирект происходит только если пользователь запросил info.html.
# Если же info.html - это результат внутреннего перенаправления, правило срабатывать не будет.
RewriteCond %{THE_REQUEST} "^(GET|POST|HEAD) /info.html HTTP/+$"
RewriteRule ^info.html$ information.html

RewriteRule ^information.html$ info.html

Анализ исходного запроса пользователя - борьба с раскрытием ссылок Apache

При обработке запроса Apache раскрывает закодированные (URL-encoded) символы из первоначального запроса. В некоторых случаях это может быть нежелательно - разработчик хочет проверять именно первоначальный, немодифицированный запрос пользователя. Сделать это можно, проверяя в RewriteCond переменную %{THE_REQUEST}:
RewriteCond %{THE_REQUEST} ^GET[\ ]+/tag/([^/]+)/[\ ]+HTTP.*$
RewriteRule ^(.*)$ index.php?tag=%1 [L]

На хабре есть обсуждение одного из таких случаев , из него и был взят вышеприведенный пример.Официальная документация Apache и особенно Technical details . Да-да.

Большое спасибо за внимание!

Теги:

Добавить метки






2024 © gtavrl.ru.