mod_rewrite для Apache

Date February 5th, 2010 Author Vitaly Agapov

“Despite the tons of examples and docs, mod_rewrite is voodoo. Damned cool voodoo, but still voodoo.”
— Brian Moore

mod_rewrite_logo
В своей недавней статье “mod_security2 для Apache” я начал тему безопасности для web-сервера Apache. Там, конечно, не было никаких откровений – простая разминка для мозгов плюс полезная памятка на будущее. Но в целом оставлять эту благодатную тему не хочется, поэтому сейчас я пробегусь по ещё одному полезному модулю – mod_rewrite.

Если вкратце, то модуль предназначен для преобразования URL’а из одного вида в другой. Кроме каких-то специфических утилитарных применений (вроде улучшения читабельности адресов, как это, меж тем, реализовано прямо здесь, на WordPress’е) модуль может повысить безопасность сервера, скрывая от пользователя параметры, передаваемые серверному сценарию. Также в контексте SEO можно упомянуть, что такие укороченные ссылки лучше индексируются поисковыми системами.

Но… ближе к телу!

Модуль mod_rewrite позволяет на лету производить  преобразования URL-адресов с использованием синтаксического анализатора с применением регулярных выражений. Модуль поддерживает неограниченное количество правил и условий для каждого правила, позволяя построить мощный и гибкий механизм преобразования. Так же как и mod_security, модуль mod_rewrite может получать информацию из HTTP-заголовков, переменных окружения, данных сервера, времени и даже из запросов к внешним базам данных.

Как устанавливать модуль, я писать не буду, здесь всё тривиально, да и в вышеупомянутой статье про mod_security я кратенько описал этот процесс.

Итак, что сделать, чтобы mod_rewrite заработал? Для начала надо удостовериться, что модуль загружается. Ищем в конфиге Апача строку вроде такой:

LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so

И заодно посмотрим в список загруженных модулей после старта Апача:

apache2ctl -M

Если увидели в списке строку rewrite_module (shared), то всё нормально.

Модуль может работать с URL входящих запросов как в контексте сервера, так и в контексте каталога. В первом случае речь идёт о правилах, прописываемых в httpd.conf или подключаемых конфигурационных файлах для всех входящих запросов. Во втором случае это правила внутри директив или в файле .htaccess. Если речь заходит о .htaccess, то здесь стоит упомянуть об одной простой вещи, которая иногда кладёт грабли зубьями вверх на пути. Кроме наличия строки AccessFileName .htaccess в конфиге следует проверить параметр AllowOverride в контексте сервера или директории, с которой мы работаем.

Модуль mod_rewrite использует два перехватчика фаз обработки HTTP-запроса, реализуемых Apache API. Первый срабатывает на преобразовании URL в имя файла в файловой системе после считывания HTTP-запроса. Здесь работают правила mod_rewrite, заданные в контексте сервера. Второй перехватчик срабатывает после авторизации и считывания конфигурационных файлов .htaccess, но до обработки содержания. Таким образом, при желании можно сделать каскадную последовательность наборов преобразований.

Директивы модуля

RewriteEngine

Значением on или off включает или выключает обработку логики mod_rewrite. Для каждого виртуального хоста должна быть своя директива RewriteEngine. Что касается наследования, то по умолчанию все вложенные директории наследуют правила преобразования от родительского каталога, если в их контексте (в .htaccess) нет директив модуля mod_rewrite. Однако же если они есть, то ничего не наследуется, а при отсутствии директивы RewriteEngine в конфигурации этой вложенной директории её значение наследуется от конфига httpd.conf (по умолчанию off). Если же надо наследовать правила от родительского каталога и добавить новые, то на помощь нам придёт директива RewriteOptions.

RewriteOptions

Значениями inherit или None включает или выключает наследование правил преобразований URL от родительского каталога.

RewriteBase

Устанавливает базовый URL для преобразований в контексте каталога. Необходим, если в конфигурации применяется Alias, приводящий к несоответствию физического пути к директории и её URL. Префикс локального каталога здесь отбрасывается, и все остальные правила работают только в оставшейся части.

RewriteCond

Определяет условие, при котором происходит преобразование. Перед директивой RewriteRule обычно располагаются одна или несколько директив RewriteCond, и преобразование происходит только при выполнении всех этих директив. Применяется в виде
RewriteCond <строка> <условие> [<флаги>]
Здесь <условие> – это Perl-совместимое регулярное выражение с некоторыми особенностями, которые опишу ниже.
<флаги> – необязательное поле в виде разделенного запятыми списка флагов:

  • ‘nocase|NC’ – независимо от регистра
  • ‘ornext|OR’ – либо следующее условие, для замены логики обработки OR вместо AND

RewriteRule

Это основная директива модуля. Она определяет правила для механизма преобразований. Применяется она в виде
RewriteRule <шаблон> <подстановка> [<флаги>]
Здесь <шаблон> – это опять Perl-совместимое регулярное выражение, применяемое к URL в том виде, в котором он дошёл до данного правила после всех предыдущих.
<флаги> – это разделённый запятыми список флагов:

  • ‘forbidden|F’ – делает URL запрещенным
  • ‘redirect|R [=code]‘ – вызывает редирект
  • ‘gone|G’ – делает URL «мёртвым» (ошибка 410 GONE)
  • ‘proxy|P’ – вызвает прокси, если подключён модуль mod_proxy
  • ‘last|L’ – последнее правило
  • ‘next|N’ – перезапустить процесс преобразований
  • ‘chain|C’ – связь со следующим правилом (если условие не выполняется, то следующее правило пропускается)
  • ‘type|T=MIME-тип’ – принудительно установить MIME тип
  • ‘nosubreq|NS’ – используется только в случае невнутреннего подзапроса
  • ‘nocase|NC’ – не учитывать регистр
  • ‘qsappend|QSA’ – добавлять к строке запроса
  • ‘noescape|NE’ – не экранировать URI при выводе
  • ‘passthrough|PT’ – пропускать через следующий обработчик (например, Alias)

RewriteLog

Определяет лог-файл для вывода событий преобразования. Должна встречаться один раз во всём конфиге сервера.

RewriteLogLevel

Определяет подробность выводимой в лог информации. Возможны значения от 0 до 9. Значения более 2 используются только для дебаггинга.

RewriteMap

Задаёт функцию поиска соответствия по ключу. Это может быть текстовый файл, скрипт или внешнее приложение. Директива задаёт имя функции, которую можно использовать впоследствии в директиве RewriteRule:

RewriteMap examplemap txt:/path/to/file/map.txt
RewriteRule ^/ex/(.*) ${examplemap:$1}

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

Во-первых, использование выделенных с помощью круглых скобок совпадений в шаблонах с помощью переменных обратных связей $N в RewriteRule и %N в RewriteCond.
Во вторых, использование переменных сервера вида %{<переменная>}.

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

HTTP-заголовки:

  • HTTP_USER_AGENT
  • HTTP_REFERER
  • HTTP_COOKIE
  • HTTP_FORWARDED
  • HTTP_HOST
  • HTTP_PROXY_CONNECTION
  • HTTP_ACCEPT

Соединение & запрос:

  • REMOTE_ADDR
  • REMOTE_HOST
  • REMOTE_USER
  • REMOTE_IDENT
  • REQUEST_METHOD
  • SCRIPT_FILENAME
  • PATH_INFO
  • QUERY_STRING
  • AUTH_TYPE

Внутренние переменные сервера:

  • DOCUMENT_ROOT
  • SERVER_ADMIN
  • SERVER_NAME
  • SERVER_ADDR
  • SERVER_PORT
  • SERVER_PROTOCOL
  • SERVER_SOFTWARE

Системные переменные:

  • TIME_YEAR
  • TIME_MON
  • TIME_DAY
  • TIME_HOUR
  • TIME_MIN
  • TIME_SEC
  • TIME_WDAY
  • TIME

Специальные:

  • API_VERSION
  • THE_REQUEST
  • REQUEST_URI
  • REQUEST_FILENAME
  • IS_SUBREQ

Особые формы условий для RewriteCond

Вместо регулярных выражений можно использовать следующие варианты:

  • ‘<Условие’ – лексически меньше
  • ‘>Условие’ – лексически больше
  • ‘=Условие’ – лексически равно
  • ‘-d’ – является ли каталогом
  • ‘-f’  -является ли обычным файлом
  • ‘-s’ – является ли обычным файлом с ненулевым размером
  • ‘-l’ – является ли символической ссылкой
  • ‘-F’ – проверка существования файла через подзапрос
  • ‘-U’ – проверка существования URL через подзапрос

Примеры

На этом памятка по теории закончена. Посмотрим кое-какие полезные примеры использования модуля mod_rewrite.

Выдача главной страницы в зависимости от браузера

RewriteEngine on
RewriteCond %{HTTP_USER_AGENT} ^Mozilla.*
RewriteRule ^/$ /homepage.max.html [L]
RewriteCond %{HTTP_USER_AGENT} ^Lynx.*
RewriteRule ^/$ /homepage.min.html [L]
RewriteRule ^/$ /homepage.std.html [L]

Авторизованным пользователям (AuthType BasicAuth) по ссылке /home выдавать содержимое их каталогов

RewriteCond %{ REMOTE _ USER } !=""
RewriteCond /home/(%{REMOTE_USER}) -d
RewriteRule (.*) /home/%1/$1

Запрет на посещение сайта Гуглботом

RewriteCond %{USER_AGENT} Googlebot
RewriteRule .* - [R=404]

Найти запрашиваемый файл в одной из двух директорий

RewriteCond /home/net/storage1/%{REQUEST_FILENAME} -f
RewriteRule (.+) /home/net/storage1/$1 [L]
RewriteCond /home/net/storage2/%{REQUEST_FILENAME} -f
RewriteRule (.+) /home/net/storage2/$1 [L]

Закрыть доступ к сайту на рабочее время

RewriteCond %{TIME_HOUR}%{TIME_MIN} >1000
RewriteCond %{TIME_HOUR}%{TIME_MIN} <1900
RewriteRule .* - [ F ]

Перенаправить несуществующий URL на другой сервер

RewriteEngine on
RewriteCond /your/docroot/%{REQUEST_FILENAME} !-f
RewriteRule ^(.+) http://webserver.сom/$1

или

RewriteEngine on
RewriteCond %{REQUEST_URI} !-U
RewriteRule ^(.+) http://webserver.сom/$1</div>

Запретить скачивание файлов с других сайтов

В этом нам поможет HTTP-заголовок Referer.

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://(www.)?mysite\.ru/.*$ [NC]
RewriteCond %{HTTP_REFERER} !.*/download\.html$
RewriteRule .(jpg|gif|png|css|zip)$ - [F]

Замена *.htm на *.html

RewriteEngine on
RewriteBase /
RewriteRule ^(.*)\.htm$  $1.html [R=permanent]

если надо ещё проверить наличие файла html, то возможен такой вариант:

RewriteEngine on
RewriteBase /
#Если .htm файл устанавливаем переменную окружения HTM=1
RewriteRule ^(.*)\.htm$ $1 [C,E=HTM:1]
#Проверяем, существует ли файл на диске
RewriteCond %{REQUEST_FILENAME}.html -f
#Если существует, делаем редирект
RewriteRule ^(.*)$ $1.html [S=1,R]
# если не существует и переменная окружения HTM=1, показываем запрошенный .htm
RewriteCond %{ENV:HTM} ^1$
RewriteRule ^(.*)$ $1.htm

Перенаправление с forum.yourdomain.net на ~/forum

RewriteEngine on
RewriteCond %{HTTP_HOST} ^forum\.yuordomain\.net$ [NC]
RewriteCond %{REQUEST_URI} !^/forum/$ [NC]
RewriteRule (.*) /forum/$1 [L]

Ссылки:

http://htaccess.net.ru/doc/mod_rewrite/mod_rewrite.php
http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html

Tags: ,
Category: Apache, Linux, Security, Web-dev | No comments »

Comments

Leave a comment

 Comment Form