Руководство по преобразованиям URL
Этот документ является дополнением к документации по mod_rewrite. Здесь описываются возможности использования mod_rewrite, входящего в состав Apache, для решения типичных практических проблем с URL, возникающих у веб-мастеров на практике. Для каждой из проблем по написанию наборов правил для URL преобразований, здесь есть детальные описания решений.
Оригинал написан
Ralf S. Engelschall < rse@apache. org>
Декабрь 1997
Введение в 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 для этих программ чтобы они знали для какой обл