mod_rewrite для Apache
Date February 5th, 2010 Author Vitaly Agapov
— Brian Moore
В своей недавней статье “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: Apache, mod_rewrite
Category:
Apache, Linux, Security, Web-dev |
No comments »