L: P:
Морда: СтандартнаяСераяЗеленая

Руководство по═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

Некоторые советы по═интерпретации:

  1. Отметьте флаг L (последний) и═отсутствие поля подстановки ('-') в═4-й части
  2. Отметьте символ ! (не) и═флаг C (цепочка) в═первом правиле последней части
  3. Отметьте шаблон перехватывающий все запросы в═последнем правиле

От═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 при запросе URL foo.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:

  1. Циклический 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. Однако конечный результат хороший , потому что общая сумма запросов действительно распределяется на═разные веб-серверы.

  2. Балансировка нагрузки с═помощью DNS

    Научный метод балансировки нагрузки основанный на═DNS это использование программы lbnamed которая может быть найдена по═ http://www.stanford.edu/~schemers/docs/lbnamed/lbnamed.html. Эта программа на═Perl 5═в═связке с═вспомогательными средствами, представляет настоящую балансировку нагрузки с═использованием DNS.

  3. Циклический 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, и═т.д. запросы, полностью выполняются другими машинами. Это основная идея.
  4. Аппаратный/TCP Round-Robin

    Для этой задачи есть также доступные аппратные решения. Cisco имеет устройство называемое LocalDirector которое производит балансировку нагрузки на═уровне TCP/IP. В═действительности это некоторый вид циклического шлюза стоящего перед веб-кластером. Если у═вас есть достаточно денег и═вас действительно нужно высокопроизводительное решение, используйте этот вариант.

Обратный 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 "&lt;b&gt;ERROR&lt;/b&gt;: 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 "&lt;b&gt;ERROR&lt;/b&gt;: 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, "&lt;$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 &lt; $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/.... В═действительности вы═можете запрограммировать здесь все что угодно. Примите во═внимание, что хотя такие массивы и═могут быть использованы, в═том числе и═обычным пользователем, однако определять их═может только системный администратор.

Valid XHTML 1.0!Valid CSS!

HIVE: All information for read only. Please respect copyright!
Hosted by hive йца: йХЕБЯЙЮЪ ЦНПНДЯЙЮЪ АХАКХНРЕЙЮ