Руководство по═URL преобразованиям
Оригинал написан
Ralf S. Engelschall <rse@apache.org>
Декабрь 1997
Перевод
Александр Егоров
<http://www.egoroff.spb.ru/>
Октябрь 2002
Этот документ дополняет документацию к═mod_rewrite
.
Здесь описываются возможности использования mod_rewrite
имеющегося у═веб-сервера Apache,
для решения типичных URL проблем, с═которыми веб-мастера обычно сталкиваются
на═практике. Для каждой из═проблем по═написанию наборов правил для URL преобразований,
здесь приводятся детальные описания решений.
Введение в═mod_rewrite
Модуль mod_rewrite
имеющийся в═составе Apache═≈ это потрясающий модуль,
т.е. это действительно
интеллектуальный модуль предоставляющий мощные средства
для URL преобразований. С═ним вы═можете делать почти все типы
URL преобразований о═которых вы═когда-либо мечтали. Цена, которую вы═должны
заплатить═≈ принять его сложность,
поскольку главный недостаток mod_rewrite
,═≈
это то, что его нелегко понять и═использовать
новичку. И═даже эксперты Apache иногда находят новые аспекты
где им═может помочь mod_rewrite
.
Другими словами: с═mod_rewrite
вы═либо сами себе прострелите ногу
при первом знакомстве, и═никогда не═будете его снова использовать,
или полюбите его на═всю оставшуюся жизнь из-за его мощи. Цель этого документа
═≈ дать вам несколько простых, работающих примеров, а═также помочь вам избежать
первого разочарования, пут╦м показа готовых решений.
Практические решения
Здесь есть масса практических решений когда-то придуманных либо мной либо другими людьми. Чувствуйте себя свободнее изучая из═этих примеров ч╦рную магию URL преобразований.
[PT]
когда дополнительно
используется mod_alias
и═mod_userdir
,
и═т.д. или переписать набор правил для их═работы в═контексте каталога═≈
.htaccess
, а═не═в═контексте сервера (httpd.conf).
Прежде чем использовать конкретный набор правил,
всегда пытайтесь понять, что же═реально делает. Так Вы═избежите проблем.Работа с═URL
Реальные URL
- Описание:
На═некоторых веб-серверах существует более одного URL для какого-либо ресурса. Обычно существуют реальные URL (которые в═действительности следует использовать и═распространять) и═те, которые просто являются ссылками, внутренними, и═т.д. Независимо от═того, какой URL пользователь применил в═сво╦м запросе, в═конце концов, он═должен увидеть только реальный URL.
- Решение:
Для всех виртуальных URL, мы═делаем внешний HTTP редирект, исправляя их═и═в═адресной строке браузера и═ во═всех последующих запросах. В═привед╦нном ниже наборе правил
/~user
заменяется реальным/u/user
и═исправляется отсутствующий завершающий слэш для/u/user
.RewriteRule ^/~([^/]+)/?(.*) /u/$1/$2 [R] RewriteRule ^/([uge])/([^/]+)$ /$1/$2/ [R]
Реальные имена хостов
- Описание:
- ┘
- Решение:
RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC] RewriteCond %{HTTP_HOST} !^$ RewriteCond %{SERVER_PORT} !^80$ RewriteRule ^/(.*) http://fully.qualified.domain.name:%{SERVER_PORT}/$1 [L,R] RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC] RewriteCond %{HTTP_HOST} !^$ RewriteRule ^/(.*) http://fully.qualified.domain.name/$1 [L,R]
Перемещенный DocumentRoot
- Описание:
Обычно
DocumentRoot
вебсервера в═ точности совпадает с═URL "/
". Однако частоDocumentRoot
выше по═иерархии нежели чем URL "/" и═в═одномDocumentRoot
могут располагаться несколько сайтов. Например для наших Intranet сайтов/e/www/
(главная страница WWW),/e/sww/
(главная страница для Intranet) и═т.д. Сейчас из-за того чтоDocumentRoot
остается "/" для/e/www/
мы═должны обеспечить работу всех подстановок картинок и═других объектов во═всех последующих запросах этой области.- Решение:
Мы═просто сделаем преобразование URL
/
в═/e/www/
. С═первого взгляда это кажется тривиальным,═≈ однако в═действительности, это тривиально только с═mod_rewrite
. Типичные старые маханизмы URL псевдонимов (что делаетсяmod_alias
и═ему подобными) использовали только совпадение по═ префиксу. В═этом случае, вы═не═можете делать такое преобразование, потому чтоDocumentRoot
является префиксом для всех URL. А═с═mod_rewrite
это действительно тривиально:RewriteEngine on RewriteRule ^/$ /e/www/ [R]
Проблема отсутствующего завершающего слэша
- Описание:
Каждый вебмастер может спеть песню о═проблеме отсутствующих завершающих слэшей при использовании URL ссылающихся на═каталоги. Если они отсутствуют, сервер выдает ошибку, потому что если вы═пишете
/~quux/foo
вместо/~quux/foo/
сервер ищет файлfoo
. И═поскольку этот файл является каталогом, происходит ошибка. В═действительности, в═большинстве случаев это исправляется само, однако, в═некоторых случаях, нужно самим эмулировать этот механизм. Например, после того, как вы═сделали массу сложных редиректов URL на═CGI скрипты и═т.д.- Решение:
Решение этой тонкой проблемы═≈ это позволить серверу добавлять завершающий слэш автоматически. Чтобы сделать это правильно, мы═должны использовать внешний редирект, для того чтобы браузер правильно запрашивал картинки и═пр. В═случае если бы═мы═ сделали только внутренний редирект, это бы═работало только для самой страницы каталога (страницы по-умолчанию), однако были бы═проблемы при наличии любых картинок на═этой странице с═относительными URL, потому что браузер сделал бы═запрос на═вставку in-lined объекта. Например, запрос для
image.gif
на═странице/~quux/foo/index.html
без внешнего редиректа выглядел бы═как/~quux/image.gif
!Поэтому, для того чтобы сделать это трюк, мы═пишем:
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo$ foo/ [R]
Сумашедший и═ленивый может даже сделать следущее в═ файле
.htaccess
находящемся в═корне веб-пространства своего сайта. Однако, следует отметить, что это создает некоторые накладные расходы.RewriteEngine on RewriteBase /~quux/ RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^(.+[^/])$ $1/ [R]
Веб-кластер через один URL
- Описание:
Мы═хотим создать одинаковый и═постоянный URL для всех WWW серверов в═Intranet веб-кластере, т.е. все URL (конкретных локальных серверов и═таким образом зависящие от═сервера!) становятся, в═действительности, независимыми от═сервера! То═что мы═хотим,═≈ так это дать пространство имен WWW постоянной независимой от═сервера схеме: никакой URL не═должен включать какой-либо реальный сервер. Кластер сам должет автоматически перенаправлять нас на═реальный сервер.
- Решение:
Первое, информация о═конкретных серверах берется из═ (распределенных) внешних массивов содержащих информацию о═том, где находятся наши пользователи, группы и═ресурсы. Имеем такую форму:
user1 server_of_user1 user2 server_of_user2 : :
Мы═помещаем эти записи в═файлы
map.xxx-to-host
. Второе, мы═должны информировать все наши серверы перенаправлять URL вида/u/user/anypath /g/group/anypath /e/entity/anypath
на
http://physical-host/u/user/anypath http://physical-host/g/group/anypath http://physical-host/e/entity/anypath
если URL не═находится на═каком-либо локальном сервере. Следующий набор правил делает это для нас при помощи файлов c═ассоциативными массивами для преобразований (примем что server0═≈ это сервер, который будет использован по-умолчанию, если в═массиве нет записи для какого-либо пользователя):
RewriteEngine on RewriteMap user-to-host txt:/path/to/map.user-to-host RewriteMap group-to-host txt:/path/to/map.group-to-host RewriteMap entity-to-host txt:/path/to/map.entity-to-host RewriteRule ^/u/([^/]+)/?(.*) http://${user-to-host:$1|server0}/u/$1/$2 RewriteRule ^/g/([^/]+)/?(.*) http://${group-to-host:$1|server0}/g/$1/$2 RewriteRule ^/e/([^/]+)/?(.*) http://${entity-to-host:$1|server0}/e/$1/$2 RewriteRule ^/([uge])/([^/]+)/?$ /$1/$2/.www/ RewriteRule ^/([uge])/([^/]+)/([^.]+.+) /$1/$2/.www/$3\
Перемещение домашних каталогов на═другой веб-сервер
- Описание:
Масса вебмастеров просили решение для следующей ситуации: Они хотели просто перенаправить все домашние каталоги (пользователей или подсайтов) с═одного сервера на═другой. Такие вещи, обычно нужны при замене старого сервера новым.
- Решение:
С═mod_rewrite решение тривиально. На═нашем старом вебсервере мы═просто сделаем преобразование всех URL вида
/~user/anypath
в═URL видаhttp://newserver/~user/anypath
.RewriteEngine on RewriteRule ^/~(.+) http://newserver/~$1 [R,L]
Структурированные домашние каталоги
- Описание:
Некоторые сайты с═тысячами пользователей обычно используют структурированную схему домашних каталогов, т.е. каждый домашний каталог находится в═каком-либо подкаталоге, который для примера начинается с═первой буквы имени пользователя. Поэтому,
/~foo/anypath
имеет путь/home/f/foo/.www/anypath
в═то═время как/~bar/anypath
имеет путь/home/b/bar/.www/anypath
.- Решение:
Следующий набор правил, используется для раскрытия в═URL символа тильды, точно в═соответствии с═вышеуказанной схемой:
RewriteEngine on RewriteRule ^/~(([a-z])[a-z0-9]+)(.*) /home/$2/$1/.www$3
Реорганизация файловой системы
- Описание:
Это действительно очень сложный пример: серь╦зная вещь, очень интенсивно использующая
RewriteRules
в═контексте каталогов(.htaccess) для того, чтобы получить приятный вид данных и═и═при просмотре их═через интернет чувство того, что структура данных никогда не═трогается или регулируется. Предыстория: net.sw это мой архив свободно распространяемого ПО═для Unix, который я═начал собирать в═1992. Для меня это одновременно и═хобби и═работа, потому что изучая компьютерную науку, многие годы, в═свободное время, я═также работал системным и═сетевым администратором. Каждую неделю мне нужно некоторое количество ПО, поэтому я═создал глубокую иерархию каталогов, где я═сохранил эти дистрибутивы:drwxrwxr-x 2 netsw users 512 Aug 3 18:39 Audio/ drwxrwxr-x 2 netsw users 512 Jul 9 14:37 Benchmark/ drwxrwxr-x 12 netsw users 512 Jul 9 00:34 Crypto/ drwxrwxr-x 5 netsw users 512 Jul 9 00:41 Database/ drwxrwxr-x 4 netsw users 512 Jul 30 19:25 Dicts/ drwxrwxr-x 10 netsw users 512 Jul 9 01:54 Graphic/ drwxrwxr-x 5 netsw users 512 Jul 9 01:58 Hackers/ drwxrwxr-x 8 netsw users 512 Jul 9 03:19 InfoSys/ drwxrwxr-x 3 netsw users 512 Jul 9 03:21 Math/ drwxrwxr-x 3 netsw users 512 Jul 9 03:24 Misc/ drwxrwxr-x 9 netsw users 512 Aug 1 16:33 Network/ drwxrwxr-x 2 netsw users 512 Jul 9 05:53 Office/ drwxrwxr-x 7 netsw users 512 Jul 9 09:24 SoftEng/ drwxrwxr-x 7 netsw users 512 Jul 9 12:17 System/ drwxrwxr-x 12 netsw users 512 Aug 3 20:15 Typesetting/ drwxrwxr-x 10 netsw users 512 Jul 9 14:08 X11/
В═июле 1996═я═решил сделать этот архив общедоступным всему миру через нормальный веб-интерфейс. "Нормальный" имеется ввиду интерфейс где вы═можете перемещаться прямо по═иерархии этого архива. И═"нормальный" значит то, что я═не═хотел ничего изменять внутри этой иерархии ═≈ даже не═помещать некоторые CGI скрипты на═е╦═ верх. Почему? Потому что вышепривед╦нная структура позже должна была быть доступна также и═через FTP, и═я═не═хотел наличия там каких-либо веб или CGI═штук.
- Решение:
Решение состоит из═2-х частей: Первое═≈ это набор CGI скриптов которые создают все страницы на═лету на═всех уровнях каталогов. Я═поместил их═в═
/e/netsw/.www/
в═следующем виде:-rw-r--r-- 1 netsw users 1318 Aug 1 18:10 .wwwacl drwxr-xr-x 18 netsw users 512 Aug 5 15:51 DATA/ -rw-rw-rw- 1 netsw users 372982 Aug 5 16:35 LOGFILE -rw-r--r-- 1 netsw users 659 Aug 4 09:27 TODO -rw-r--r-- 1 netsw users 5697 Aug 1 18:01 netsw-about.html -rwxr-xr-x 1 netsw users 579 Aug 2 10:33 netsw-access.pl -rwxr-xr-x 1 netsw users 1532 Aug 1 17:35 netsw-changes.cgi -rwxr-xr-x 1 netsw users 2866 Aug 5 14:49 netsw-home.cgi drwxr-xr-x 2 netsw users 512 Jul 8 23:47 netsw-img/ -rwxr-xr-x 1 netsw users 24050 Aug 5 15:49 netsw-lsdir.cgi -rwxr-xr-x 1 netsw users 1589 Aug 3 18:43 netsw-search.cgi -rwxr-xr-x 1 netsw users 1885 Aug 1 17:41 netsw-tree.cgi -rw-r--r-- 1 netsw users 234 Jul 30 16:35 netsw-unlimit.lst
Подкаталог
DATA/
содержит вышеприведенную структуру каталогов, т.е. настоящие данные net.sw и═автоматически, время от═времени, получает обновления черезrdist
. Вторая часть проблемы остается: как связать эти две структуры вместе в═одно нормальное дерево URL? Мы═хотим скрыть каталогDATA/
от═пользователя при запуске соответствующих CGI скриптов для разных URL. Вот и═решение: сначала я═поместил следующее в═конфигурационный файл каталога в═DocumentRoot
сервера для преобразования объявленного URL/net.sw/
во═внутренний путь/e/netsw
:RewriteRule ^net.sw$ net.sw/ [R] RewriteRule ^net.sw/(.*)$ e/netsw/$1
Первое правило предназначено для запросов у═который отсутствует завершающий слэш! Второе правило делает нужное преобразование. И═затем, в═конфигурационном файле каталога
/e/netsw/.www/.wwwacl
, производится полное конфигурирование:Options ExecCGI FollowSymLinks Includes MultiViews RewriteEngine on # устанавливем префикс /net.sw/ RewriteBase /net.sw/ # сначала мы перенаправляем все запросы к корневому каталогу на # управляющий cgi скрипт RewriteRule ^$ netsw-home.cgi [L] RewriteRule ^index\.html$ netsw-home.cgi [L] # обрезаем подкаталоги когда # браузер запрашивает нас со страниц в подкаталогах RewriteRule ^.+/(netsw-[^/]+/.+)$ $1 [L] # и теперь не делаем редирект для локальных файлов RewriteRule ^netsw-home\.cgi.* - [L] RewriteRule ^netsw-changes\.cgi.* - [L] RewriteRule ^netsw-search\.cgi.* - [L] RewriteRule ^netsw-tree\.cgi$ - [L] RewriteRule ^netsw-about\.html$ - [L] RewriteRule ^netsw-img/.*$ - [L] # все остальное является подкаталогом и управляется # другим cgi скриптом RewriteRule !^netsw-lsdir\.cgi.* - [C] RewriteRule (.*) netsw-lsdir.cgi/$1
Некоторые советы по═интерпретации:
- Отметьте флаг
L
(последний) и═отсутствие поля подстановки ('-
') в═4-й части - Отметьте символ
!
(не) и═флагC
(цепочка) в═первом правиле последней части - Отметьте шаблон перехватывающий все запросы в═последнем правиле
- Отметьте флаг
От═NCSA imagemap к═Apache mod_imap
- Описание:
При переходе с═веб-сервера NCSA, на═более современный веб-сервер Apache, масса народу хотят чтобы, этот переход был безболезненный. Поэтому они хотят, чтобы страницы, использующие старую программу NCSA
imagemap
, работали и═под Apache с═новым модулемmod_imap
. Проблема в═том, что есть много гиперссылок, ссылающихся на═программуimagemap
через/cgi-bin/imagemap/path/to/page.map
. В═ Apache необходимо прочитать только/path/to/page.map
.- Решение:
Для всех запросов, мы═используем глобальное правило удаления на═лету ненужного префикса:
RewriteEngine on RewriteRule ^/cgi-bin/imagemap(.*) $1 [PT]
Поиск страниц больше чем в═одном каталоге
- Описание:
Иногда необходимо позволить веб-серверу искать страницы больше чем в═одном каталоге. Здесь вам не═помогут ни═MultiViews ни═ другие техники.
- Решение:
Мы═пишем явный набор правил который ищет файлы в═каталогах.
RewriteEngine on # во-первых попытаемся найти это в указанном месте/... # ...и если нашли то заканчиваем поиск и сидим и радуемся: RewriteCond /your/docroot/dir1/%{REQUEST_FILENAME} -f RewriteRule ^(.+) /your/docroot/dir1/$1 [L] # во-вторых - попытаемся найти это в pub/... # ...и если нашли то заканчиваем поиск и сидим и радуемся: RewriteCond /your/docroot/dir2/%{REQUEST_FILENAME} -f RewriteRule ^(.+) /your/docroot/dir2/$1 [L] # иначе продолжаем для других директив Alias или ScriptAlias, # и т.д. RewriteRule ^(.+) - [PT]
Установка переменных окружения в═соответствии с═частями URL
- Описание:
Возможно вы═хотите хранить информацию о═статусе между запросами и═использовать URL для е╦═кодирования. Однако не═хочется, просто для получения этой информации, использовать CGI обработчик всех страниц.
- Решение:
Мы═используем правило для того чтобы скрывать информацию о═статусе и═хранить е╦═в═переменной окружения которая затем может быть использована в═XSSI или CGI. Таким образом URL
/foo/S=java/bar/
преобразуется в═/foo/bar/
и═переменная окруженияSTATUS
устанавливается в═"java".RewriteEngine on RewriteRule ^(.*)/S=([^/]+)/(.*) $1/$3 [E=STATUS:$2]
Виртуальные хосты пользователей
- Описание:
Предположим═≈ вы═хотите предоставлять адреса
www.username.host.domain.com
для страниц пользователей через записи DNS типа A═на═той же═самой машине и═без каких либо виртуальных хостов на═этой машине.- Решение:
Для запросов HTTP/1.0 решения нет, однако для запросов HTTP/1.1 которые содержат HTTP заголовок Host: мы═ можем использовать следующий набор правил для преобразования
http://www.username.host.com/anypath
во═внутренний путь/home/username/anypath
:RewriteEngine on RewriteCond %{HTTP_HOST} ^www\.[^.]+\.host\.com$ RewriteRule ^(.+) %{HTTP_HOST}$1 [C] RewriteRule ^www\.([^.]+)\.host\.com(.*) /home/$1$2
Перенаправление домашних каталогов для чужаков
- Описание:
Мы═хотим перенаправить URL домашних каталогов на═другой веб-сервер
www.somewhere.com
когда запрашиваемый пользователь не═принадлежит локальному доменуourdomain.com
. Это иногда используется в═ контексте виртуальных хостов.- Решение:
Просто правило на═перенаправление:
RewriteEngine on RewriteCond %{REMOTE_HOST} !^.+\.ourdomain\.com$ RewriteRule ^(/~.+) http://www.somewhere.com/$1 [R,L]
Перенаправление несуществующих URL на═другой веб-сервер
- Описание:
Типичный часто задаваемый вопрос по═URL преобразованиям═≈ это как перенаправить несуществующие запросы с═сервера А═на═сервер B. Обычно это делается через
ErrorDocument
CGI-скрипты на═Perl, однако с═модулемmod_rewrite
тоже есть решение. Заметьте однако, что это менее ресурсо╦мко чем использваниеErrorDocument
CGI-скрипта!- Решение:
Первое решение имеет лучшую производительность однако меньшую гибкость и═меньшую защиту от═ошибок:
RewriteEngine on RewriteCond /your/docroot/%{REQUEST_FILENAME} !-f RewriteRule ^(.+) http://webserverB.dom/$1
Проблема здесь в═том, что это будет работать только для страниц находяшихся внутри
DocumentRoot
. Тогда как вы═можете добавить больше условий (например ещ╦ и═для управления домашними каталогами, и═т.д.) есть лучший вариант:RewriteEngine on RewriteCond %{REQUEST_URI} !-U RewriteRule ^(.+) http://webserverB.dom/$1
Здесь используется функция опережающей проверки URL (look-ahead), присутствующая в═
mod_rewrite
. В═результате это будет работать для всех типов URL и═к═тому же═это безопасно. Однако это снижает производительность веб-сервера, потому что для каждого запроса производится более одного внутреннего подзапроса. Поэтому, если ваш веб-сервер имеет мощный процессор, используйте этот вариант. Если это медленная машина, используйте первый или лучшеErrorDocument
CGI-скрипта.
Внешний редирект
- Описание:
Иногда нам нужно больше контроля над URL (касаемо механизма обрезания символов) при преобразованиях. Обычно обрезающие функции URL ядра Apache также убирают и═якоря, т.е. в═ URL вида "url#anchor". Вы═не═можете это непосредственно использовать при редиректах с═
mod_rewrite
потому что функция Apache uri_escape() также обрезала бы═срезаемый символ. Как мы═можем сделать редирект такому URL?- Решение:
Мы═должны использовать ляп в═программе используя NPH-CGI скрипт который делает редирект на═себя самого. Потому что здесь не═делается обрезание (NPH=non-parseable headers). Сначала мы═вводим новую URL scheme
xredirect:
в═следующей строке конфигурационного файла сервера (должно быть одной из═последних директив ):RewriteRule ^xredirect:(.+) /path/to/nph-xredirect.cgi/$1 \ [T=application/x-httpd-cgi,L]
Это направляет все URL начинающиеся с═
xredirect:
через программуnph-xredirect.cgi
. И═эта программа выглядит примерно так :#!/path/to/perl ## ## nph-xredirect.cgi -- NPH/CGI script for extended redirects ## Copyright (c) 1997 Ralf S. Engelschall, All Rights Reserved. ## $| = 1; $url = $ENV{'PATH_INFO'}; print "HTTP/1.0 302 Moved Temporarily\n"; print "Server: $ENV{'SERVER_SOFTWARE'}\n"; print "Location: $url\n"; print "Content-type: text/html\n"; print "\n"; print "<html>\n"; print "<head>\n"; print "<title>302 Moved Temporarily (EXTENDED)</title>\n"; print "</head>\n"; print "<body>\n"; print "<h1>Moved Temporarily (EXTENDED)</h1>\n"; print "The document has moved <a HREF=\"$url\">here</a>.<p>\n"; print "</body>\n"; print "</html>\n"; ##EOF##
Это предоставляет вам функции для того чтобы делать перенаправления на═все URL schemes, т.е. включая те, которые, не═прямо принимаются
mod_rewrite
. Для примера вы═можете сейчас также перенаправитьnews:newsgroup
черезRewriteRule ^anyurl xredirect:news:newsgroup
Замечание: Вы═не═должны использовать[R]
или[R,L]
в═вышеприведенной директиве потому чтоxredirect:
позже, должен быть продолжен нашим специальным правилом "pipe through" которое приведено выше.
Мультиплексор для доступа к═архивам
- Описание:
Вы═знаете такую классную вещь как CPAN (Comprehensive Perl Archive Network) расположенную по═адресу http://www.perl.com/CPAN? Здесь работает редирект на═один из═нескольких FTP серверов по═всему миру, на═которых расположено зеркало CPAN и═они приблизительно расположены рядом с═местоположением запращивающего клиента. В═действительности это может быть названо мультиплексирующей службой доступа к═FTP. Хотя CPAN работает на═CGI скриптах, как можно реализовать похожее решение с═помощью
mod_rewrite
?- Решение:
Для начала отметим что начиная с═версии 3.0.0═
mod_rewrite
также может использовать "ftp:" префикс при редиректах. И═второе, апроксимация местоположения может быть сделана путем ассоциативного массива преобразованийRewriteMap
через домен верхнего уровня, для конкретного клиента. С═помощью хитрой цепочки директив мы═ можем использовать этот домен верхнего уровня в═качестве ключа для поиска в═ нашем ассоциативном массиве мультеплексирования.RewriteEngine on RewriteMap multiplex txt:/path/to/map.cxan RewriteRule ^/CxAN/(.*) %{REMOTE_HOST}::$1 [C] RewriteRule ^.+\.([a-zA-Z]+)::(.*)$ ${multiplex:$1|ftp.default.dom}$2 [R,L]
## ## map.cxan -- массив мультиплексирования для CxAN ## de ftp://ftp.cxan.de/CxAN/ uk ftp://ftp.cxan.uk/CxAN/ com ftp://ftp.cxan.com/CxAN/ : ##EOF##
Редиректы в═зависимости от═времени
- Описание:
Когда нужно применять уловки типа содержания зависящего от═времени масса вебмастеров все ещ╦ используют CGI скрипты которые производят редиректы на═специальные страницы. Как это может быть сделано через
mod_rewrite
?- Решение:
Есть много переменных названных
TIME_xxx
для условий редиректа. В═связке со═специальными лексикографическими образцами для сравнения<STRING
,>STRING
и═=STRING
мы═можем производить редиректы зависящие от═времени:RewriteEngine on RewriteCond %{TIME_HOUR}%{TIME_MIN} >0700 RewriteCond %{TIME_HOUR}%{TIME_MIN} <1900 RewriteRule ^foo\.html$ foo.day.html RewriteRule ^foo\.html$ foo.night.html
Это выдает содержимое
foo.day.html
при запросе URLfoo.html
с═07:00 до═19:00 а═ в═оставшееся время содержимоеfoo.night.html
. Просто класная вещь для какой-либо странички┘
Обратная совместимость при миграции с═YYYY на═XXXX
- Описание:
Как возможно сделать URL обратно совместимыми (все ещ╦ существующими виртуально) после миграции документа
document.YYYY
в═документdocument.XXXX
, т.е. после преобразования кучи.html
файлов в═файлы.phtml
?- Решение:
Мы═просто оставляем без изменения имя файла и═проверяем существование нового расширения. Если оно существует, мы═оствляем это без изменений, иначе мы═перенаправляем URL в═его реальное местоположение.
# набор правил для обратной совместимости при # редиректе для document.html в document.phtml # тогда и только тогда когда document.phtml существует # одноко не является document.html RewriteEngine on RewriteBase /~quux/ # вычленяем базовое имя, и запоминаем его RewriteRule ^(.*)\.html$ $1 [C,E=WasHTML:yes] # делаем редирект на document.phtml если он существует RewriteCond %{REQUEST_FILENAME}.phtml -f RewriteRule ^(.*)$ $1.phtml [S=1] # иначе делаем откат на предыдущий документ RewriteCond %{ENV:WasHTML} ^yes$ RewriteRule ^(.*)$ $1.html
Управление содержанием
От═старого с═новому (внутреннее)
- Описание:
Предположим что мы═недавно переименовали страницу
bar.html
в═foo.html
и═сейчас хотим для обратной совместимости сделать доступным и═старый URL. В═действительности мы═хотим чтобы пользователи использующие старый URL даже не═узнали что страницы были переименованы.- Решение:
Мы═перенаправим старый URL на═новый через внутренний редирект путем следующих директив:
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html
От═старого с═новому (внешнее)
- Описание:
Снова предположим что мы═недавно переименовали страницу
bar.html
в═foo.html
и═хотим сейчас для обратной совместимости сделать доступным и═старый URL. Однако, в═этот раз мы═хотимчтобы пользователи использующие старый URL узнали этот новый URL, т.е. адресная строка их═браузеров также должна измениться.- Решение:
Мы═используем HTTP редирект на═новый URL который приведет к═ к═изменениям в═браузерах(в адресной строке) и═таким образом это видят пользователи:
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html [R]
Содержимое зависимое от═браузера
- Описание:
Иногда, по═крайней мере для важных страниц верхнего уровня необходимо предоставить оптимизированное для конкретного браузера содержание, т.е. для одного нужно выдавать полную версию для последних версий Netscape, минимальную для браузеров Lynx и═промежуточную для всех других.
- Решение:
Мы═не═можем использовать content negotiation потому что браузеры не═ не═представляют свой тип в═этой форме. Вместо этого мы═должны использовать HTTP заголовок "User-Agent". Следующее условие делает следующее: Если HTTP заголовок "User-Agent" начинается с═"Mozilla/3", страница
foo.html
преобразуется в═foo.NS.html
и═редирект прекращается. Если браузер "Lynx" или "Mozilla" версий 1═или 2═URL становитсяfoo.20.html
. Все остальные браузеры получают страницуfoo.32.html
. Это делается следующим набором директив:RewriteCond %{HTTP_USER_AGENT} ^Mozilla/3.* RewriteRule ^foo\.html$ foo.NS.html [L] RewriteCond %{HTTP_USER_AGENT} ^Lynx/.* [OR] RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[12].* RewriteRule ^foo\.html$ foo.20.html [L] RewriteRule ^foo\.html$ foo.32.html [L]
Динамическое зеркало
- Описание:
Предположим что есть чудесные страницы на═удал╦нных хостах и═мы═хотим внести их═в═наше пространство имен(сайт). Для FTP серверов мы═бы═использовали программу
зеркало
которая в═действительности управляет обновлениями копий удал╦нных данных на═локальной машине. Для веб-сервера мы═могли бы═использовать программуwebcopy
которая делает похожие вещи по═HTTP. Однако обе эти технологии имеют один главный недостток: локальная копия актуальна всегда настолько, насколько часто мы═запускаем эту программу. Было бы═ намного лучше если бы═зеркало было не═статическим должно быть полное соответствие копий, вне зависимости от═частоты запуска этой программы. Вместо этого мы═хотим динамическое зеркало с═автоматическим обновлением данных когда это необходимо (обновление данных на═удаленном сервере).- Решение:
Для обеспечения этой функции мы═отобразим удаленную страницу или даже полностью удаленный сайт в═наше веб-пространство используя Proxy Throughput опцию (флаг
[P]
):RewriteEngine on RewriteBase /~quux/ RewriteRule ^hotsheet/(.*)$ http://www.tstimpreso.com/hotsheet/$1 [P]
RewriteEngine on RewriteBase /~quux/ RewriteRule ^usa-news\.html$ http://www.quux-corp.com/news/index.html [P]
Обратное динамическое зеркало
- Описание:
- ┘
- Решение:
RewriteEngine on RewriteCond /mirror/of/remotesite/$1 -U RewriteRule ^http://www\.remotesite\.com/(.*)$ /mirror/of/remotesite/$1
Получение отсутствующих данных из═Intranet
- Описание:
Это хитрый способ виртуального корпоративного (внешнего) Internet веб-сервера (
www.quux-corp.dom
), в═действительности хранящем и═управляющем данными расположенными на═(внутреннем) Intranet веб-сервере (www2.quux-corp.dom
) которые защищены firewall'ом. Фокус в═том, что на═внешнем веб-сервере мы═ получаем запрошенные данные на═лету с═внутреннего сервера.- Решение:
Сначала, мы═должны убедиться что наш firewall все ещ╦ защищает внутренний веб-сервер и═что только внешнему веб-серверу разрешено получать с═него данные. Для firewall'a с═фильтрацией пакетов мы═могли бы═для примера сконфигурировать набор директив для firewall подобно этому:
ALLOW Host www.quux-corp.dom Port >1024 --> Host www2.quux-corp.dom Port 80
DENY Host * Port * --> Host www2.quux-corp.dom Port 80Просто подправьте это чтобы соответствовать вашему синтаксису. Теперь мы═можем использовать директивы
mod_rewrite
которые запрашивают отсутствующие данные с═backend сервера через прокси:RewriteRule ^/~([^/]+)/?(.*) /home/$1/.www/$2 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^/home/([^/]+)/.www/?(.*) http://www2.quux-corp.dom/~$1/pub/$2 [P]
Балансировка нагрузки
- Описание:
Предположим что мы═хотим сделать балансировку нагрузки для траффика
www.foo.com
на═www[0-5].foo.com
(всего 6═серверов). Как это возможно сделать?- Решение:
Есть масса возможных решений этой проблемы. Мы═обсудим сначала общеизвестный основанный на═DNS вариант и═специфический с═помощью
mod_rewrite
:- Циклический DNS
Самый простой способ для балансировки нагрузки это использование функции циклического DNS в═DNS сервере BIND. Просто сконфигурируйте
www[0-9].foo.com
как обычно в═своем DNS сервере с═использованием записей типа A(address) т.е.www0 IN A 1.2.3.1 www1 IN A 1.2.3.2 www2 IN A 1.2.3.3 www3 IN A 1.2.3.4 www4 IN A 1.2.3.5 www5 IN A 1.2.3.6
Затем, дополнительно добавьте следующую запись:
www IN CNAME www0.foo.com. IN CNAME www1.foo.com. IN CNAME www2.foo.com. IN CNAME www3.foo.com. IN CNAME www4.foo.com. IN CNAME www5.foo.com. IN CNAME www6.foo.com.
Заметьте что это кажется неправильным, однако в═действительности это характерная особенность BIND и═может быть использована таким способом. Однако, теперь, когда происходит разрешение
www.foo.com
, BIND выдаетwww0-www6
═≈ однако слегка меняя/перемещая каждый раз порядок. Таким образом клиенты распределяются по═разным серверам . Однако заметьте что это не═блестящая схема балансировки нагрузки, потому что информация о═разрешении имен DNS кэшируется другими серверами имен в═сети, поэтому при первом разрешении имениwww.foo.com
клиентом на═конкретныйwwwN.foo.com
, все последующие запросы также пойдут на═это конкретное доменное имяwwwN.foo.com
. Однако конечный результат хороший , потому что общая сумма запросов действительно распределяется на═разные веб-серверы. - Балансировка нагрузки с═помощью DNS
Научный метод балансировки нагрузки основанный на═DNS это использование программы
lbnamed
которая может быть найдена по═ http://www.stanford.edu/~schemers/docs/lbnamed/lbnamed.html. Эта программа на═Perl 5═в═связке с═вспомогательными средствами, представляет настоящую балансировку нагрузки с═использованием DNS. - Циклический Proxy
В═этом варианте мы═используем
mod_rewrite
и═его proxy функцию. Сначала мы═сопоставимwww0.foo.com
реальномуwww.foo.com
при помощи простойwww IN CNAME www0.foo.com.
записи в═DNS. Затем мы═направляем
www0.foo.com
только на═proxy сервер, т.е. мы═настраиваем эту машину так чтобы все входные URL просто пропускались через внутренний proxy на═один из═ 5-ти других серверов (www1-www5
). Для этого мы═сначала введем набор директив которые взаимодействуют со═скриптомlb.pl
балансировки нагрузки для всех URL.RewriteEngine on RewriteMap lb prg:/path/to/lb.pl RewriteRule ^/(.+)$ ${lb:$1} [P,L]
Затем мы═пишем
lb.pl
:#!/path/to/perl ## ## lb.pl -- скрипт балансировки нагрузки ## $| = 1; $name = "www"; # база для имени хоста $first = 1; # первый сервер (здесь не 0, потому что 0 это и есть этот сервер) $last = 5; # последний сервер в цикле $domain = "foo.dom"; # доменное имя $cnt = 0; while (<STDIN>) { $cnt = (($cnt+1) % ($last+1-$first)); $server = sprintf("%s%d.%s", $name, $cnt+$first, $domain); print "http://$server/$_"; } ##EOF##
Последнее замечание: Почему это полезно? Кажется чтоwww0.foo.com
все-ещ╦ перегружен? Ответ положительный ,═≈ он═перегружен, однако только простыми proxy запросами! Все SSI, CGI, ePerl, и═т.д. запросы, полностью выполняются другими машинами. Это основная идея. - Аппаратный/TCP Round-Robin
Для этой задачи есть также доступные аппратные решения. Cisco имеет устройство называемое LocalDirector которое производит балансировку нагрузки на═уровне TCP/IP. В═действительности это некоторый вид циклического шлюза стоящего перед веб-кластером. Если у═вас есть достаточно денег и═вас действительно нужно высокопроизводительное решение, используйте этот вариант.
- Циклический DNS
Обратный Proxy
- Описание:
- ┘
- Решение:
## ## apache-rproxy.conf -- Конфигурация Apache для использования обратного Proxy ## # тип сервера ServerType standalone Listen 8000 MinSpareServers 16 StartServers 16 MaxSpareServers 16 MaxClients 16 MaxRequestsPerChild 100 # параметры функционирования сервера KeepAlive on MaxKeepAliveRequests 100 KeepAliveTimeout 15 Timeout 400 IdentityCheck off HostnameLookups off # пути для рабочих файлов PidFile /path/to/apache-rproxy.pid LockFile /path/to/apache-rproxy.lock ErrorLog /path/to/apache-rproxy.elog CustomLog /path/to/apache-rproxy.dlog "%{%v/%T}t %h -> %{SERVER}e URL: %U" # неиспользуемые пути ServerRoot /tmp DocumentRoot /tmp CacheRoot /tmp RewriteLog /dev/null TransferLog /dev/null TypesConfig /dev/null AccessConfig /dev/null ResourceConfig /dev/null # повышаем скорость работы и безопасность <Directory /> Options -FollowSymLinks -SymLinksIfOwnerMatch AllowOverride None </Directory> # страница состояния для контроля обратного proxy <Location /apache-rproxy-status> SetHandler server-status </Location> # включаем механизм URL преобразований RewriteEngine on RewriteLogLevel 0 # определяем ассоциативный массив редиректов со списками величин в которых # mod_rewrite произвольно выбирает какую-либо из них RewriteMap server rnd:/path/to/apache-rproxy.conf-servers # проверка того что страница состояния обрабатывается локально # и проверка того, что никто не использует наш proxy кроме нас самих RewriteRule ^/apache-rproxy-status.* - [L] RewriteRule ^(http|ftp)://.* - [F] # теперь выбираем возможные серверы для конкретных типов URL RewriteRule ^/(.*\.(cgi|shtml))$ to://${server:dynamic}/$1 [S=1] RewriteRule ^/(.*)$ to://${server:static}/$1 # и делегируем сгенерированный URL передавая его # через proxy-модуль RewriteRule ^to://([^/]+)/(.*) http://$1/$2 [E=SERVER:$1,P,L] # и гарантируем что все другие ресурсы запрещены # когда они прошли через вышестоящие правила... RewriteRule .* - [F] # включаем Proxy-модуль без кэширования ProxyRequests on NoCache * # устанавливаем обратное отображение URL для ответов при редиректе ProxyPassReverse / http://www1.foo.dom/ ProxyPassReverse / http://www2.foo.dom/ ProxyPassReverse / http://www3.foo.dom/ ProxyPassReverse / http://www4.foo.dom/ ProxyPassReverse / http://www5.foo.dom/ ProxyPassReverse / http://www6.foo.dom/
## ## apache-rproxy.conf-servers -- таблица выбора Apache/mod_rewrite ## # список backend серверов которые обслуживают статические # страницы (HTML файлы и картинки, и т.д.) static www1.foo.dom|www2.foo.dom|www3.foo.dom|www4.foo.dom # список backend серверов которые обслуживают динамически # сгенерированные страницы (CGI программы или mod_perl скрипты) dynamic www5.foo.dom|www6.foo.dom
Новый тип MIME, новая служба
- Описание:
В═сети есть масса отличных CGI программ. Однако их═применение обычно надоедает, поэтому многие веб-мастера их═не═используют. Даже функция Action handler Apache'а для MIME-типов применима только тогда, когда CGI программы не═нуждаются в═специальных URL (в действительности
PATH_INFO
и═QUERY_STRINGS
) для них является входом. Сначала, давайте настроим новый тип файла с═расширением.scgi
(для защищенных CGI) которые будут обрабатываться популярной программойcgiwrap
. Проблема здесь заключается в═том что для этого случая мы═используем однородный URL (см. выше) и═и═какой-либо файл находящийся внутри домашних каталогов пользователей имеет URL/u/user/foo/bar.scgi
. Однакоcgiwrap
требует URL в═виде/~user/foo/bar.scgi/
. Следующая директива решает эту проблему:RewriteRule ^/[uge]/([^/]+)/\.www/(.+)\.scgi(.*) ... ... /internal/cgi/user/cgiwrap/~$1/$2.scgi$3 [NS,T=application/x-http-cgi]
Или предположим что у═нас есть несколько полезных программ:
wwwlog
(отображающаяaccess.log
для поддерева URL и═wwwidx
(запускающая Glimpse на═поддереве URL ). Мы═должны предоставить область URL для этих программ чтобы они знали для какой области они должны работать. Однако обычно это ужасно, потому что они все время запрашиваются из═тех областей, т.е. обычно мы═бы═ запускали программуswwidx
из═/u/user/foo/
через ссылку/internal/cgi/user/swwidx?i=/u/user/foo/
которая ужасна. Так как мы═должны жестко связывать и местоположение области и местонахождение CGI внутри гиперссылки. В═случае если нам необходимо реорганизовать эту область, мы═тратим массу времени меняя различные гиперссылки.
- Решение:
Решение в═этом случае это сделать новый специальный формат URL который автоматически приводит к═вызову нужного CGI. Настраиваем так:
RewriteRule ^/([uge])/([^/]+)(/?.*)/\* /internal/cgi/user/wwwidx?i=/$1/$2$3/ RewriteRule ^/([uge])/([^/]+)(/?.*):log /internal/cgi/user/wwwlog?f=/$1/$2$3
Теперь гиперссылка для поиска
/u/user/foo/
считывает толькоHREF="*"
который внутренне автоматически трансформируется в
/internal/cgi/user/wwwidx?i=/u/user/foo/
То═же═самое приводит к═вызову CGI программы для доступа к═логам когда используется гиперссылка
:log
.
От═статики к═динамике
- Описание:
Как можно трансформировать статическую страницу
foo.html
в═е╦═динамический вариантfoo.cgi
незаметным образом, т.е. так чтобы ни═браузер ни═пользователь не═заметили этого.- Решение:
Мы═просто перенаправляем URL на═CGI-скрипт и═корректируем MIME-тип так чтобы это действительно работало как CGI-скрипт. Таким образом запрос к═
/~quux/foo.html
внутренне приведет к═вызову/~quux/foo.cgi
.RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ foo.cgi [T=application/x-httpd-cgi]
Регенерация содержания на═лету
- Описание:
Здесь рассматривается действительно вещь для посвященных: Динамически созданные однако статически обслуживаемые страницы, т.е. страницы которые должны передаваться как чисто статические (считываемые из═файловой системы и═затем передаваемые по═запросу), однако они должны быть динамически сгенерированны веб-сервером если они отсутствуют в═файловой системе. Таким образом вы═можете иметь страницы сгенерированные CGI которые являются статически обслуживаемыми если только кто-либо (либо планировщик) не═удалит статическое содержание. В═таком случае содержание обновляется.
- Решение:
-
Это делается следующим набором директив:
RewriteCond %{REQUEST_FILENAME} !-s RewriteRule ^page\.html$ page.cgi [T=application/x-httpd-cgi,L]
Здесь, запрос к═
page.html
приводит к═ внутреннему запуску соответствующегоpage.cgi
еслиpage.html
все-ещ╦ отсутствует или имеет нулевой размер. Фокус здесь в═том чтоpage.cgi
это обычный CGI скрипт который (в дополнение к═собственномуSTDOUT
) записывает свой вывод в═файлpage.html
. Запустив это один раз, сервер передает данныеpage.html
. Когда вебмастер хочет обновить содержание, он═просто удаляетpage.html
(обычно с═помощью cronjob).
Документ с═автоматическим обновлением
- Описание:
Разве это было бы═не═здорово при создании сложной страницы если бы═ браузер автоматически обновлял эту страницу каждый раз когда мы═создаем новую е╦═версию в═нашем редакторе? Невозможно?
- Решение:
Нет! Мы═просто скомбинируем опцию составного MIME веб-сервера, опцию NPH вебсервера и═мощь
mod_rewrite
при URL преобразованиях. Сначала, мы═реализуем новую опцию URL: Простое добавление:refresh
к═любому URL является причиной его обновления каждый раз когда происходит его обновление в═файловой системе.RewriteRule ^(/[uge]/[^/]+/?.*):refresh /internal/cgi/apache/nph-refresh?f=$1
Теперь когда мы═ссылаемся на═URL
/u/foo/bar/page.html:refresh
происходит внутренний вызов URL
/internal/cgi/apache/nph-refresh?f=/u/foo/bar/page.html
Единственная отсутствующая часть это NPH-CGI скрипт. Хотя обычно говорится что "это остается для упражения читателю" ;-) Я═также здесь приведу и═этот код.
#!/sw/bin/perl ## ## nph-refresh -- NPH/CGI script for auto refreshing pages ## Copyright (c) 1997 Ralf S. Engelschall, All Rights Reserved. ## $| = 1; # split the QUERY_STRING variable @pairs = split(/&/, $ENV{'QUERY_STRING'}); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $name =~ tr/A-Z/a-z/; $name = 'QS_' . $name; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; eval "\$$name = \"$value\""; } $QS_s = 1 if ($QS_s eq ''); $QS_n = 3600 if ($QS_n eq ''); if ($QS_f eq '') { print "HTTP/1.0 200 OK\n"; print "Content-type: text/html\n\n"; print "<b>ERROR</b>: No file given\n"; exit(0); } if (! -f $QS_f) { print "HTTP/1.0 200 OK\n"; print "Content-type: text/html\n\n"; print "<b>ERROR</b>: File $QS_f not found\n"; exit(0); } sub print_http_headers_multipart_begin { print "HTTP/1.0 200 OK\n"; $bound = "ThisRandomString12345"; print "Content-type: multipart/x-mixed-replace;boundary=$bound\n"; &print_http_headers_multipart_next; } sub print_http_headers_multipart_next { print "\n--$bound\n"; } sub print_http_headers_multipart_end { print "\n--$bound--\n"; } sub displayhtml { local($buffer) = @_; $len = length($buffer); print "Content-type: text/html\n"; print "Content-length: $len\n\n"; print $buffer; } sub readfile { local($file) = @_; local(*FP, $size, $buffer, $bytes); ($x, $x, $x, $x, $x, $x, $x, $size) = stat($file); $size = sprintf("%d", $size); open(FP, "<$file"); $bytes = sysread(FP, $buffer, $size); close(FP); return $buffer; } $buffer = &readfile($QS_f); &print_http_headers_multipart_begin; &displayhtml($buffer); sub mystat { local($file) = $_[0]; local($time); ($x, $x, $x, $x, $x, $x, $x, $x, $x, $mtime) = stat($file); return $mtime; } $mtimeL = &mystat($QS_f); $mtime = $mtime; for ($n = 0; $n < $QS_n; $n++) { while (1) { $mtime = &mystat($QS_f); if ($mtime ne $mtimeL) { $mtimeL = $mtime; sleep(2); $buffer = &readfile($QS_f); &print_http_headers_multipart_next; &displayhtml($buffer); sleep(5); $mtimeL = &mystat($QS_f); last; } sleep($QS_s); } } &print_http_headers_multipart_end; exit(0); ##EOF##
Массовый виртуальный хостинг
- Описание:
Опция
<VirtualHost>
Apache очень хороша и═великолепно работает когда у═вас есть немного виртуальных хостов. Однако когда в═являетесь ISP и═имеете сотни виртуальных хостов это является не═лучшим выбором для такой задачи.- Решение:
Для решения этой задачи мы═создадим ассоциации страниц на═виртуальных хостах или даже полностью всего веб-пространства виртуальных хостов, с═ресурсами в═нашем пространстве имен(ресурсами основного сервера) с═использованием Proxy функции (флаг
[P]
):## ## vhost.map ## www.vhost1.dom:80 /path/to/docroot/vhost1 www.vhost2.dom:80 /path/to/docroot/vhost2 : www.vhostN.dom:80 /path/to/docroot/vhostN
## ## httpd.conf ## : # используем каноническое имя хоста при редиректах, и т.д. UseCanonicalName on : # добавляем впереди виртуальный хост формата CLF CustomLog /path/to/access_log "%{VHOST}e %h %l %u %t \"%r\" %>s %b" : # включаем механизм mod_rewrite на основном сервере RewriteEngine on # определяем два массива: один для коррекции URL и другой для определения # доступных виртуальных хостов с соответствующим # DocumentRoot. RewriteMap lowercase int:tolower RewriteMap vhost txt:/path/to/vhost.map # Теперь делаем реальные ассоциации с виртуальными хостами # через большое и сложное одиночное правило: # # 1. проверяем что мы не делаем ассоциации с собственными URL # (принадлежащими основному серверу) RewriteCond %{REQUEST_URL} !^/commonurl1/.* RewriteCond %{REQUEST_URL} !^/commonurl2/.* : RewriteCond %{REQUEST_URL} !^/commonurlN/.* # # 2. проверяем что мы имеем заголовок хоста, потому что # сейчас наша единственная задача это поддерживать # виртуальный хостинг через этот заголовок RewriteCond %{HTTP_HOST} !^$ # # 3. переводим имя хоста в нижний регистр RewriteCond ${lowercase:%{HTTP_HOST}|NONE} ^(.+)$ # # 4. ищем имя этого хоста в vhost.map и # запоминаем его только тогда, когда оно является пут╦м # (а не "NONE" см. выше) RewriteCond ${vhost:%1} ^(/.*)$ # # 5. наконец мы можем ассоциировать этот URL с местоположением его реального # document root # и запоминаем имя виртуального хоста для записи в журнал RewriteRule ^/(.*)$ %1/$1 [E=VHOST:${lowercase:%{HTTP_HOST}}] :
Ограничение доступа
Блокирование роботов
- Описание:
Как мы═можем заблокировать действительно надоедливых роботов на═ получение страниц из═специфических областей сайта? Файла
/robots.txt
содержащего записи "Robot Exclusion Protocol" обычно недостаточно для избавления от═такого робота.- Решение:
Мы═используем набор правил запрещающие доступ для роботов к═URL из═ веб-пространства
/~quux/foo/arc/
(возможно это пространство имеет очень глубокую иерархию каталогов, обход которой роботом, привел бы═к═очень большой нагрузке на═сервер). Мы═должны убедиться что запрещается доступ только для конкретного робота, т.е. просто запрет для хоста с═которого работает робот недостаточен. Это, также блокировало бы═доступ пользователей с═этого хоста. Этого эффекта мы═добьемся проверяя также информацию из═HTTP заголовока User-Agent.RewriteCond %{HTTP_USER_AGENT} ^NameOfBadRobot.* RewriteCond %{REMOTE_ADDR} ^123\.45\.67\.[8-9]$ RewriteRule ^/~quux/foo/arc/.+ - [F]
Блокирование вставки графических файлов
- Описание:
Предположим что мы═имеем на═
http://www.quux-corp.de/~quux/
некоторые страницы на═которых у═нас есть графические изображения в═формате GIF. Эти графические изображения очень хороши, поэтому другие могут помещать их═на═свои страницы прямо через гиперссылки. Нам не═нравится эта практика потому что она добавляет бесполезный траффик для нашего сервера.- Решение:
В═то═время как мы═не═можем на═100%═защитить наши картинки от═вставки на═другие страницы, мы, по═крайней мере, можем ограничить случаи в═которых браузер посылает HTTP заголовок Referer.
RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !^http://www.quux-corp.de/~quux/.*$ [NC] RewriteRule .*\.gif$ - [F]
RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !.*/foo-with-gif\.html$ RewriteRule ^inlined-in-foo\.gif$ - [F]
Запрет хоста
- Описание:
Как мы═можем запретить доступ к═нашему серверу для списка внешних хостов?
- Решение:
Для Apache версий >= 1.3b6:
RewriteEngine on RewriteMap hosts-deny txt:/path/to/hosts.deny RewriteCond ${hosts-deny:%{REMOTE_HOST}|NOT-FOUND} !=NOT-FOUND [OR] RewriteCond ${hosts-deny:%{REMOTE_ADDR}|NOT-FOUND} !=NOT-FOUND RewriteRule ^/.* - [F]
Для Apache версий <= 1.3b6:
RewriteEngine on RewriteMap hosts-deny txt:/path/to/hosts.deny RewriteRule ^/(.*)$ ${hosts-deny:%{REMOTE_HOST}|NOT-FOUND}/$1 RewriteRule !^NOT-FOUND/.* - [F] RewriteRule ^NOT-FOUND/(.*)$ ${hosts-deny:%{REMOTE_ADDR}|NOT-FOUND}/$1 RewriteRule !^NOT-FOUND/.* - [F] RewriteRule ^NOT-FOUND/(.*)$ /$1
## ## hosts.deny ## ## ВНИМАНИЕ! Это ассоциативный массив, а не список, даже если мы его относим к списку. ## mod_rewrite бер╦т из него пары ключ/значение, поэтому, для каждой записи, ## должно быть представлено по крайней мере фиктивное значение "-". ## 193.102.180.41 - bsdti1.sdm.de - 192.76.162.40 -
Запрет Proxy
- Описание:
Как мы═можем запретить конкретному хосту или даже пользователю со═ специального хоста использовать proxy нашего Apache?
- Решение:
Сначала мы═должны убедиться что вызов
mod_rewrite
ниже(!) чем вызовmod_proxy
в═конфигурационном файле при сборке Apache. Таким образом он═вызывается передmod_proxy
. Затем мы═ настраиваем следующий запрет зависящий от═хоста┘RewriteCond %{REMOTE_HOST} ^badhost\.mydomain\.com$ RewriteRule !^http://[^/.]\.mydomain.com.* - [F]
┘и═следующий запрет зависящий от═пользователя с═конкретного хоста:
RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} ^badguy@badhost\.mydomain\.com$ RewriteRule !^http://[^/.]\.mydomain.com.* - [F]
Вариант специальной аутенфикации
- Описание:
Иногда необходима очень специфичная аутенфикация, например аутенфикация проверяющая принадлежность к═списку особых пользователей. Только они могут иметь доступ к═какому-либо ресурсу и═при этом не═должно быть окна запроса предлагающего ввести данные для аутенфикации (которое появляется при использовании базовой аутенфикации через
mod_auth_basic
).- Решение:
Мы═используем список условий для для исключения всех кроме разрешенных лиц:
RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} !^friend1@client1.quux-corp\.com$ RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} !^friend2@client2.quux-corp\.com$ RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} !^friend3@client3.quux-corp\.com$ RewriteRule ^/~quux/only-for-friends/ - [F]
Отражатель основанный на═HTTP заголовке Referer
- Описание:
Как мы═можем запрограммировать гибкий отражатель URL который работает полагаясь на═HTTP заголовок "Referer" и═может быть настроен на═такое количество ссылающихся страниц, какое мы═хотим?
- Решение:
Используйте следующий, действительно хитроумный набор правил┘
RewriteMap deflector txt:/path/to/deflector.map RewriteCond %{HTTP_REFERER} !="" RewriteCond ${deflector:%{HTTP_REFERER}} ^-$ RewriteRule ^.* %{HTTP_REFERER} [R,L] RewriteCond %{HTTP_REFERER} !="" RewriteCond ${deflector:%{HTTP_REFERER}|NOT-FOUND} !=NOT-FOUND RewriteRule ^.* ${deflector:%{HTTP_REFERER}} [R,L]
┘ в═связке с═соответствующим ассоциативным массивом преобразований:
## ## deflector.map ## http://www.badguys.com/bad/index.html - http://www.badguys.com/bad/index2.html - http://www.badguys.com/bad/index3.html http://somewhere.com/
Это автоматически перенаправит запрос назад на═ ссылающуюся страницу (когда "
-
" используется в═качестве значения в═массиве) или на═конкретный URL (когда в═массиве в═качестве значения указан какой-либо URL).
Другое
Внешний механизм преобразований
- Описание:
Часто задаваемый вопрос: Как мы═можем решить проблему FOO/BAR/QUUX/и т.д.? Здесь кажется нет решения с═использованием
mod_rewrite
┘- Решение:
Использование внешнего массива
RewriteMap
, т.е. программы которая действует какRewriteMap
. Она запускается один раз при старте Apache, получает запрошенные URL на═входной поток
и═должна помещать результирующиие URL (обычно преобразованные) в═выходной поток
(в том же═самом порядке!).RewriteEngine on RewriteMap quux-map prg:/path/to/map.quux.pl RewriteRule ^/~quux/(.*)$ /~quux/${quux-map:$1}
#!/path/to/perl # отключение буфферизованного I/O который привел бы # к бесконечному зацикливанию и зависанию Apache $| = 1; # чтение URL один в строке из входного потока и # генерация URL для подстановки в выходной поток while (<>) { s|^foo/|bar/|; print $_; }
Это только демонстрационный пример и═тут просто все URL
/~quux/foo/...
преобразуются в═/~quux/bar/...
. В═действительности вы═можете запрограммировать здесь все что угодно. Примите во═внимание, что хотя такие массивы и═могут быть использованы, в═том числе и═обычным пользователем, однако определять их═может только системный администратор.