Материалы для WEB разработки на LAMP
Шпаргалка по модулю mod_rewrite сервера Apache

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

Модуль mod_rewrite - это модуль сервера Apache, предоставляющий мощный функционал для выполнения различных преобразований над URL, которые Apache выполняет на лету. Этот модуль содержит синтаксический анализатор URL с возможностью применения регулярных выражений. Также модуль позволяет использовать при анализе URL не только сам URL, но и разные другие источники данных, как например переменные сервера, переменные окружения, HTTP заголовки, время и даже(!) запросы к внешним базам данных в разных форматах. Практически это значит, что получив URL Вы сможете синтаксически разобрать его на любые части как вы этого захотите. Затем вы сможете выполнить сравнения, с применением множества условий, любых частей вашего URL с большим количеством доступных параметров как из окружения сервера Apache так и ваших собственных, подставленных напрямую, так и полученных из баз данных. Затем, в зависимости от результатов сравнения, вы сможете выполнить различные преобразования над текущей строкой URL (с которой модуль в текущий момент работает) и даже сгенерировать части строки URL. Как следствие, на выходе вы можете получить новую строку URL, и теперь сервер Apache уже будет искать запрошенную страницу не по первоначальному URL, а уже по новому измененному вами URL. В добавок к этому вы можете определить или переопределить поведение apache при обработки вашего нового URL.  При помощи специальных флагов вы можете задать для apache как ему следует обрабатывать этот новый URL. Все эти действия в результате приведут или к внутренней обработке нового URL, или к внешнему перенаправлению запроса, или даже к прохождению его через внутренний прокси модуль –то все определите вы. Далее приведены некоторые из возможных поведений при обработке нового URL. Так как новый URL может быть любым - как внутренним, так и внешним, то произойдет внутренне или внешнее перенаправление с первоначального URL на ваш конечный преобразованный URL. При внутреннем перенаправлении, когда ваш новый URL ссылается на тот же сайт, что и в первоначальном URL, вы можете выполнить внутреннее перенаправление не изменяя строку адреса в браузере клиента, т.е. клиент даже не заметит, что на сервере произошло внутреннее перенаправление (обработка) его запроса, и он обратился по одному URL, а в действительности ответ он получил от другого URL. Для клиента будет виден, в этом случае, только URL по которому он обратился, о наличии внутреннего перенаправления он сможет только догадываться. Такое внутреннее перенаправление достаточно распространенный подход для систем управления контентом, когда все запросы к файлам PHP сайта перенаправляются на главный index.php системы управления контентом. Также внутренние перенаправление можно выполнить сделав изменение URL в браузере клиента с отправкой ему кода заголовка перенаправления (redirect), например, кода 301 Moved Permanently - постоянное перенаправление. Когда же новый, преобразованный вами URL будет уже ссылаться на другой сайт, то произойдет внешнее перенаправление. Также вы можете направить запрос на внутренний прокси сервер. Вы также можете определить и другие варианты поведения для нового URL, например, отказать в выдачи файла и т.п., вариантов поведения которые можно задать специальными флагами достаточно, чтобы обеспечить все необходимые варианты.

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


Вот эти постулаты:

•    Модуль оперирует с полными URL (включая path-info) и в контексте сервера apache и в контексте каталога (.htaccess) и даже может генерировать части строки запроса в качестве результата.  Практически это значит следующее,  если, например, вы используете директивы модуля mod_rewrite в файле .htaccess, расположенном в корне вашего сайта, то исходный URL (до каких либо преобразований) будет начинаться от корня вашего сайта.

•    Правило преобразования URL (RewriteRule директива) это условие и правило одновременно. Если вы посмотрите на синтаксис RewriteRule директивы, то увидите, что она содержит условие, которому должен соответствовать текущий URL, что бы это правило преобразования начало выполняться, и само по себе правило преобразования (это то как изменить текущий URL).  Здесь под словом правило подразумевается некое выражение, согласно которому будет выполнено изменение URL. Я, для наглядности, что бы избежать путаницы со словом «правило» сказал бы,  что RewriteRule содержит условие и алгоритм изменения URL, который называют правилом изменения URL.  Т.е. что бы правило начало выполняться (именно начало, т.к. по ходу выполнения возможны разные варианты и не всегда это приведет к указанному в правиле преобразованию URL) должно выполняться заданное в правиле условие для этого URL. Иными словами, правило преобразования срабатывает и начинает выполняться только если текущий URL соответствует условию из этого правила. Здесь можно провести аналогию для директивы RewriteRule как бы с подпрограммой, которая запускается по условию и выполняет некие манипуляции, в том числе и изменение URL. Но результат выполнения этой подпрограммы не всегда изменение URL. Т.е. нужно понимать, что запушенное правило преобразования не обязательно приведет к изменению URL, по ходу его работы возможны разные варианты исполнения правила, которые задаются дополнительными условиями. Об этом следующий пункт.

•    К правилу (RewriteRule) помимо условия, содержащегося в самом правиле (это условие запускает исполнение самого правила) можно задать дополнительные условия при помощи директив RewriteCond.  Дополнительных условий может быть несколько (несколько строк с RewriteCond директивами). Вот тут начинается разрыв шаблона. Но не пугайтесь, сейчас все объясню.  Зачем нужны дополнительные условия и почему их не вставить в правило сразу? Тут дело в том, что условие в правиле, если оно выполняется, только запускает процесс исполнения правила, а дополнительные условия начинают проверяться только в ходе исполнения правила и позволяют управлять ходом исполнения этого правила! Таким образом дополнительные условия позволяют уже в процессе выполнения правила определить выполнить ли в конечном итоге преобразования этого несчастного URL или нет. И, еще один разрыв шаблона, записываются эти дополнительные условия (RewriteCond)  не после самого правила (как было бы логично), а перед правилом(RewriteRule). Это выглядит нелогично и когда начинаешь разбираться с этим в первый раз сбивает с толку. Но такая запись дополнительных условий перед правилом объясняется историческими причинами. Просто так сложилось. Да, это не логично, но примите это как данность и запомните, что дополнительные условия (RewriteCond директивы) записываются ПЕРЕД их правилом (RewriteRule), а не после, и начинают эти условия проверяться только тогда, когда правило запустилось на выполнение. О логике исполнения именно дополнительных условий ниже.

•    Понятие текущего URL. Здесь под «текущим» подразумевается значение URL, когда проверяется и применяется текущее правило. Этот URL не обязательно совпадает с первоначально запрошенным URL, потому что любое количество правил возможно уже были применены к нему и соответственно преобразовали изначальный URL, т.к. этот модуль может выполнять несколько последовательно следующих друг за другом преобразований URL. Это значит, что если вы указали несколько правил (RewriteRule) для преобразования URL, то все те правила, которые соответствуют указанным в них условиям, будут выполнять изменения (преобразования) URL последовательно от предыдущего правила к следующему правилу. Отсюда очень важный постулат: первичный URL, который, так сказать, пошел по этапам обработки будет меняться от одного выполненного правила к другому, и каждое последующее правило будет начинать работать (проверять на условие и изменять) уже НЕ с первичной строкой URL, а уже с той измененной строкой, которая получилась на выходе от применения предыдущего правила(преобразования). Это очень важно понимать, что каждое последующее правило преобразования работает НЕ с первичной строкой URL, а работает со строкой URL уже преобразованной предыдущим исполненным правилом (именно исполненным правилом, т.е. правилом которое последним выполнило преобразование).

•    Порядок расположения правил (RewriteRule) в файле имеет значение. Как видите из выше описанного, порядок расположения правил обработки URL имеет важное значение, т.к. URL передается от правила к правилу.  Также Порядок правил в наборе важен еще потому, что механизм преобразований обрабатывает их по следующей логике. Сначала механизм преобразований просматривает последовательно весь набор правил строчка за строчкой (RewriteRule директивы) и когда он встречает правило (RewriteRule) условие из которого применительно к текущему URL является истинным, то механизм преобразований начинает исполнять это правило. Если же условие из правила (RewriteRule) ложно для текущего URL, то механизм преобразования НЕ исполняет это правило, а переходит к следующему правилу.

•    Логика исполнения правила (RewriteRule). Исполнение же правила подразумевает следующие действия: первым делом механизм преобразования выполняет поиск дополнительных условий для этого правила (RewriteCond директивы). Помним, что по историческим причинам дополнительные условия находятся перед правилами(RewriteRule). Если дополнительные условия для этого правила отсутствуют, то механизм преобразований тупо выполняет указанное в правиле преобразование текущего URL и переходит к следующему правилу. Однако если для исполняемого правила (RewriteRule) существуют дополнительные условия, указанные ПЕРЕД НИМ в директивах RewriteCond, то запускается внутренний цикл для обработки этих дополнительных условий в том порядке, в котором они перечислены, сверху вниз. Если из имеющихся для правила дополнительных условий хотя бы одно условие НЕ выполняется это приводит к остановке запущенного процесса исполнения правила, и преобразование над URL, заданное в правиле, НЕ выполняется. Что бы запущенное на исполнение правило выполнилось до конца и изменило URL, необходимо, что бы выполнились ВСЕ дополнительные условия, указанные в директивах RewriteCond перед этим правилом! Тут нужно дополнительно пояснить, что директивы RewriteCond по умолчанию объединены между собой оператором AND в одно составное условие. Просто этот оператор(AND) не записывается по умолчанию. От сюда и такая логика, что нужно, что бы все дополнительные условия были истинными (т.к. они объедены через AND) для удачного завершения преобразования URL. Однако директивы RewriteCond можно объединить условием OR при помощи флагов (см. синтаксис директивы). Про это нужно помнить, при задании дополнительных условий.

•    После исполнения правила можно дополнительно переопределить алгоритм обработки измененного URL сервером apache. Здесь имеется в виду то, что вы можете после изменения URL при помощи специальных флагов указать для apache нужные вам действия, например отказать в выдачи файла, или выполнить ридирект на новый URL.

•    Нужно помнить,  что mod_rewrite является частью apache,  это значит, что вы можете применить его функционал (директивы) на разных уровнях конфигурации apache. На глобальном уровне, путем записи директив в главный конфиг apache или в конфиги подключаемые к нему, на уровне виртуального хоста путем добавления директив в файлы конфигурации виртуальных хостов, и на уровне каталога путем добавления директив в файл. htaccess.

Логическая схема исполнения правила (RewriteRule)


Теперь, когда мы рассмотрели главные постулаты и логику обработки правила, рассмотрим синтаксис директив.


Синтаксис директивы RewriteRule:

RewriteRule Шаблон Подстановка [Флаги]

Пример:

RewriteRule ^(.*)$ index.php?/$1 [L,QSA]

Где:

1.    RewriteRule  – это название директивы – правила.
2.    Шаблон - это как раз то условие, выполнение которого запускает исполнение правила. Это условие - Шаблон представляет собой perl совместимое регулярное выражение, которое применяется к текущему URI без query string с GET параметрами.
3.    Подстановка – это как раз тот алгоритм изменения URL, т.е. правило изменения (преобразования) URL.
4.    [Флаги] подстановки - Третий аргумент директивы RewriteRule.

[Флаги] RewriteRule Flags - это разделённый запятыми, заглавные спец символы, заключенные в квадратные скобки. Флаги дополняют преобразование URL.

В параметрах Подстановка из RewriteRule и СравниваемаяСтрока из RewriteCond доступны для использования подвыражения (то что заключено в скобки в РВ) - части регулярного выражения. Обращаться к подвыражениям в условиях RewriteCond нужкно как %N и в подвыражений в правилах RewriteRule как $N. Здесь N - это номер подвыражения по порядку в строке с регулярным выражением.

Параметры в правиле записываются в одну строку начиная с имени директивы и отделяются друг от друга пробелом.

Теперь давайте разберем пример правила RewriteRule:

RewriteRule ^(.*)$ index.php?/$1 [L,QSA]

1.    ^(.*)$ - это Шаблон, регулярное выражение которое применится к текущему URI (без query string с GET параметрами). Кто знаком с регулярными выражении, сможет прочитать его так: искать в текущей строке URI от начала строки(знак ^) до конца строки(знак $) любой символ (знак .) в количестве от нуля до бесконечности (знак *). Так как в этом РВ есть скобки(), то та часть URI которая будет соответствовать условию в скобках будет захвачена в переменную подстановки $1, которую мы потом сможем использовать в Подстановке или в СравниваемаяСтрока(это в директиве RewriteCond, о ней ниже).
2.     index.php?/$1 - Подстановка – это собственно и есть правило преобразования URL. Запись index.php?/$1 означает, что строка нового, преобразованного URL должна быть составлена из двух частей, где первая часть строки это постоянное значение index.php?/, а вторая часть строки это значение из переменной подстановки $1. Тут мы вспоминаем что в $1 храниться та часть URL которая соответствовала части регулярного выражения в скобах из параметра Шаблон. В итоге мы получаем преобразованный URL вида index.php?/URL_по которому обратились. По другому сказать так, что для всех запросов выполняется внутренне перенаправление на файл index.php.
3.    [L,QSA] флаги, где 'last|L' - последнее правило что означает - остановить процесс преобразования на этом месте и не применять больше никаких следующих правил преобразований для URL.

Флаг 'qsappend|QSA' - значит добавить в конец нового URI исходную строку GET параметров запроса QUERY_STRING, которая содержится в серверной переменной %{QUERY_STRING}. Этот флаг указывает механизму преобразований на добавление, а не замену, строки параметров GET запроса из URI к строке Подстановка. Дело в том, что если ваш начальный URI содержит query string с GET параметрами, например: /pages/123?one=two, то по умолчанию RewriteRule преобразование отбрасывает эту query string с GET параметрами от URI. Если же добавить к RewriteRule флаг [QSA], то тогда первоначальная query string с GET параметрами будет добавлена в конец нового URI, преобразованного в RewriteRule.

Пример:

RewriteRule "/pages/(.+)" "/page.php?page=$1" [QSA]

В примере, при наличии [QSA] флага, запрошенный URI вида /pages/123?one=two будет преобразован в /page.php?page=123&one=two. Однако, без использования [QSA] флага, он уже будет преобразован в /page.php?page=123, где уже будет отсутствовать оригинальная первичная query string с GET параметрами.

 

Ну вот мы разобрали синтаксис простого примера директивы RewriteRule, теперь дело за синтаксисом директивы RewriteCond

Синтаксис RewriteCond:

RewriteCond СравниваемаяСтрока Условие [flags]

Где:
1.    СравниваемаяСтрока это строка которая будет проверятся на соответствие выражению, указанному в параметре Условие. СравниваемаяСтрока может, к примеру, содержать часть или весь URL. Подставить в параметр СравниваемаяСтрока часть URL можно при помощи переменных подстановки, которые были созданы в соответствующем RewriteRule. Также параметр СравниваемаяСтрока может содержать различные переменные (Server-Variables) из окружения web сервера Apache, например:

  • %{REQUEST_URI} - Строка URI запроса - это часть от полного URL без доменного имени и GET параметров вида "/pages/page/1/";
  • %{HTTP_HOST} - Строка с доменным именем, например: "andew.ru" или "www.andew.ru";
  • %{QUERY_STRING}    Строка GET параметров ,например, "?page=123&one=two".

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

2.    Условие – это логическое выражение по которому проверяется параметр СравниваемаяСтрока. Часто в Условие применяют РВ.

3.    [flags] - третьим аргументом в директиве RewriteCond. Флаг позволяет задать дополнительные опиции, например, можно установить логику объединения правил RewriteCond через логическое И [AND] (по умолчанию) или через логическое ИЛИ [OR], или/и можно задать, будет ли сравнение в условии RewriteCond выполнятся с учетом регистра или без учета регистра, и много другое. Некоторые наиболее часто используемые флаги в условиях RewriteCond описаны мною ниже.

Пример1 - пример правила с набором условий:

RewriteCond $1 !^(index\.php|images|robots\.txt|public) [NC]

RewriteCond %{REQUEST_URI} !\.(css|js|jpg|gif|png)$

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.*)$ index.php?/$1 [L,QSA]

В примере1 мы видим, что директиве RewriteRule соответствует 4-е дополнительных условия (RewriteCond). Если правило начнет исполняться (об этом см. синтаксис правила), то механизм преобразований начнет по очереди обходить каждое из этих дополнительных условий, начиная с первого. Помним, что дополнительные условия стоят перед правилом. Если все дополнительные условия выполняться, произойдет преобразование URL. Если хотя бы одно дополнительное условие не выполниться, исполнение правила будет остановлено и преобразования URL не произойдет. Тут нужно заметить, что дополнительные условия объединены по умолчанию через оператор AND, хотя он явно не указан. Поэтому то и необходимо исполнение всех дополнительных условий.


Теперь разберем первое дополнительное условие из примера1:

RewriteCond $1 !^(index\.php|images|robots\.txt|public) [NC]


Где:
1.    параметр СравниваемаяСтрока содержит выражение $1 – это переменная подстановки, в которой находится значение из круглых скобок из параметра Шаблон директивы RewriteRule. Если мы посмотрим в параметр Шаблон то там содержится: ^(.*)$. Все это означает в данном случае, что в параметр СравниваемаяСтрока подставиться весь URL.
2.    Параметр Условие здесь содержит оператор инверсии (!)условия и регулярное выражение, которое вернет нам результат применения его к параметру СравниваемаяСтрока. В данном случае параметр Условие звучит как: не начинается с символов index.php или images или robots.txt или public.
3.    Флаг [NC] - 'nocase|NC' выполнять сравнение регистр независимо
В общем, это дополнительное условие будет звучать как: если без учета регистра URL не начинается с символов index.php или images или robots.txt или public то условие истинно.

Теперь давайте словами опишу блок директив из примера1.

Этот блок директив содержит одно правило преобразования URL и дополнительные условия для этого правила. Смысл этого блока – выполнить заданную проверку и преобразование URL. На словах это будет звучать так. Для любого URL запустить выполнение правила перезаписи URL. По ходу выполнение правила проверить что URL:
•    НЕ начинается как index.php или images или robots.txt или public – т.е. не начинается на открытые нами к прямому обращению файл и каталоги. Сравнение символов URL на соответствие в этом условии проводить без учета регистра.
•    НЕ заканчивается на .css или .js или … т.е. не является файлом к которым мы позволяем обращаться напрямую
•    НЕ является реальным файлом на диске
•    НЕ является реальной директорией на диске
Если все условия выполнились то сделать следующие преобразование URL – собрать итоговую строку URL из двух строк, где первая строка это “ index.php?/”, вторя строка это первоначальный URL. Флаг L – значит закончить на этом правиле все дальнейшие преобразования URL. Флаг QSA значит добавить значение из $1 к результирующей строке URL, а не заменить ее.
Вот такой получился блок обработки URL.

 

Пример2:

RewriteCond %{REMOTE_HOST} ^host1.* [OR]

RewriteCond %{REMOTE_HOST} ^host2.* [OR]

RewriteCond %{REMOTE_HOST} ^host3.*

RewriteRule ...

Это пример демонстрирует как можно при помоши флага [OR] объединить дополнительные условия (RewriteCond). Тут надо заметить, что нет флага [AND], а директивы (RewriteCond) записанные без флагов [OR] по умолчанию объединяются условием [AND], хотя явно оператор AND не прописывается. Об этом нужно не забывать.

 

Пример корневого файла .htaccess для сайта:

#пример файла .htacces

AddDefaultCharset UTF-8
Options -Indexes +FollowSymLinks
DirectoryIndex index.php index.html

#Блок если нужно ограничить доступ по IP
# смотри статью "Контроль доступа клиента в Apache "
#Order Deny,Allow
#Deny from all
#Allow from 127.0.0.1 192.168.1 ...

#Блок правил для модуля mod_rewrite
<IfModule mod_rewrite.c>
  RewriteEngine On

  #переопределить корень сайта, станет как "/"
  RewriteBase /

  #Все с HTTP на HTTPS
  RewriteCond %{HTTPS} =off
  RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,QSA,L]
#или как: RewriteRule ^(.*)$ https://%{HTTP_HOST}%/$1 [R=301,QSA,L]

  #Все что не файл, не директория, не картинка и т.п. - то на index.php
  RewriteCond %{REQUEST_URI} !^/(index\.php|images|robots\.txt|public) [NC]
  RewriteCond %{REQUEST_URI} !\.(cssіjsіjpgіgifіpng)$
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule ^(.*)$ /index.php?$1 [L,QSA]
#или как: RewriteRule . /index.php [L] 

#hotlink защита НЕ забывайте про экранирование в РВ
RewriteCond %{HTTP_REFERER} !^$
    RewriteCond %{HTTP_REFERER} !^http://site\.ru/ [NC]
    RewriteCond %{HTTP_REFERER} !^https://site \.ru/ [NC]
    RewriteCond %{HTTP_REFERER} !^http://www\.site\.ru/ [NC]
    RewriteCond %{HTTP_REFERER} !^https://www\.site\.ru/ [NC]
    RewriteRule \.(jpeg|png|bmp|gif|jpg|js|css)$ - [F]
</IfModule>

*Распространенные форматы файлов: jpeg|png|bmp|gif|jpg|js|css|mp4|mp3|webm|m4a|ogg|ogv|swf|webp|avi|mov|mpeg|mpc|mpg|tif|tiff|vtt|flv|gz

 

RewriteRule \.(jpeg|png|bmp|gif|jpg|js|css)$ - [F]—это значит что для URI заканчивающиеся на .расширение из скобок - ничего не изменять (тире после РВ как параметр Подстановка значит НЕ изменять URL), не перезаписывать URL и [F] значит отказать в выдаче файла.

Примеры:

#редирект с www.site.ru на site.ru без www
RewriteCond %{HTTP_HOST} ^www\.(.*) [NC]
RewriteRule ^(.*)$ http://%1/$1 [R=301,QSA,L]

Выполняется проверка доменного имени, и если домен начинается с www, то работает правило: все, на http://%1/$1. Где %1 это домен без www из условия в RewriteCond, а $1 это URI  из самого RewriteRule правила.

#редирект с site.ru на www.site.ru
RewriteCond %{HTTP_HOST} ^(.*)$ [NC]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ http://www.%1/$1 [R=301,QSA,L]

Простые редиректы

#Простой внешний редирект, можно применять
#без RewriteCond, только с RewriteRule
RewriteRule ^pages/page.* /about.html [R=301,QSA,L]

Внутренний реврайт

#Внутренний реврайт, т.е. просто перезапись URI
RewriteRule ^pages/page.* /about.html [L]
#еще
RewriteRule ^index.php$  /about-us/ [L]

Редирект на основе GET параметров

#Пример редиректа со страницы
#/?do=page&id=15 был редирект на /page/15/
RewriteCond %{QUERY_STRING} do=page [NC]
RewriteCond %{QUERY_STRING} id=(\d+) [NC]
RewriteRule .* /page/%1/? [R=301,L]

Редирект с обычной на мобильную версию сайта

RewriteCond %{HTTP_HOST} ^(.*)$ [NC]
RewriteCond %{HTTP_USER_AGENT} (?i:midp|samsung|nokia|j2me|avant|docomo|novarra|palmos|palmsource|opwv|chtml|pda|mmp|blackberry|mib|symbian|wireless|nokia|hand|mobi|phone|cdm|upb|audio|SIE|SEC|samsung|HTC|mot-|mitsu|sagem|sony|alcatel|lg|eric|vx|NEC|philips|mmm|xx|panasonic|sharp|wap|sch|rover|pocket|benq|java|pt|pg|vox|amoi|bird|compal|kg|voda|sany|kdd|dbt|sendo|sgh|gradi|jb|dddi|moto|iphone|android) [NC]
RewriteRule ^$  http://m.%1 [R=302,L]

Еще примеры правил можно посмотреть в статье

 

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

Также не забывайте оборачивать весь блок правил для  mod_rewrite в тег: <IfModule mod_rewrite.c></IfModule>

Не пишите излишне много директив для mod_rewrite, пишите только те правила преобразования, которые вам действительно необходимы. Особенно нужно быть аккуратным с внешними ридиректами - это такие ридиректы, которые выполняются путем отправки клиенту серверного заголовка с кодом НЕ 200 (отдача полноценной страницы), а с другим кодом (чаше всего 301 и 302) и которые приводят к перенаправлению в браузере клиента на другой URL т.е. к совершению нового запроса на клиенте. Поэтому любой внешний ридирект всегда приводит к потере времени в обработки запроса, т.к. нужно отправить клиенту ответ, он должен его прочитать, и повторить запрос уже по новому URL. Это затратная по времени процедура. Поэтому ридиректы должны быть только если они действительно вам необходимы.

Учитывайте, что браузеры могут кешировать редиректы, при этом Ctrl+F5 или Ctrl+R не снимает проблему. Поэтому отключайте кеширование в браузере при тестировании ваших правил rewrite модуля web сервера Apache.

Для поиска ошибок работы ваших правил читайте логи Apache.

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

В этой статье я описал только две директивы и главные на мой взгляд понятия. Однако, как сами понимаете, mod_rewrite предоставляет много других директив и функционала.

Андрей Болдырев