По следам виртуальных директорий, или как можно настроить VirtualHost
11.12.2001
От автора. Если завтра мне понадобится настроить Apache для работы с очередным виртуальным сервером, я буду использовать именно эту "рыбу".
Здесь просуммирован опыт нескольких специалистов с которыми в свое время мне приходилсь общаться, за что огромное спасибо!
Конечно жизнь не стоит на месте? будут появляться новые проблемы, их решения, будут меняться взгляды, и обстоятельства, но надеюсь мои развернутые комментарии помогут Вам понять суть и обоснование всех пунктов, а значит Вы сможете и дальше уже самостоятельно двигаться в нужном направлении...
"На берегу" хотелось бы отметить один момент ? автор давно уже придерживается идеи о том, что обращения ко всем документам (исключая статические), в пределах одного сайта, должны сводиться в одну точку (н.п. /index.html). Исключения, конечно, бывают, но они, как обычно, лишь подтверждают правило.
Сделать это можно несколькими путями:
- созданием нужной файловой структуры, где все файлы делают только лишь
include(файл_движок);
- созданием нужной файловой структуры, с помощью линков* на файл_движок (кстати, это самый привычный и высокопроизводительный вариант, хотя и имеющий организационные ограничения в плане поддержания достаточно динамической структуры, например, дерево каталога большой торговой фирмы);
- поддержание виртуальной файловой структуры с помощью Web-сервера (здесь тоже возможны варианты, и один из них, для Apache, самый высокопроизводительный, эффективный и гибкий, описан ниже);
* ? "жесткие" линки, работают не только в Unix/Linux-системах, но и в Windows NT, Windows 2000, Windows XP (используйте Far с Alt+F6 или специальные утилиты).
<VirtualHost *>
# обратите внимание - для сайта отведена домашняя директория
# при этом всё, что видно через браузер пользователю находится в поддиректории /html/
DocumentRoot "/www/имя_сайта/html" ServerAdmin webmaster@имя_сайта.ru # хороший тон, когда сайт откликается и на полное и на укороченное имя
ServerName www.имя_сайта.ru ServerAlias имя_сайта.ru
# вот еще использование домашней директории - поддиректория для лог-файлов
# удобно, что они всегда под рукой разработчика (некоторые провайдеры
# ВООБЩЕ не предоставляют доступ к логам! :( - это очень неудобно),
# и в тоже время, они недоступны пользователю, т.к. находятся вне DocumentRoot
ErrorLog "/www/имя_сайта/logs/error_log" CustomLog "/www/имя_сайта/logs/access_log" common # Кстати, "common" конечно не единственный формат логов, более того -
# есть возможность самому задать его структуру
# (см. http://httpd.apache.org/docs/mod/mod_log_config.html).
# пользоваль комфортнее себя чувствует видя привычное расширение .html
# в пользу расширения .html и другие аргументы:
# - при сохранение в браузере страницы, пользователь получит файл с
# таким расширением, а потом легко его откроет по Enter`у;
# - если Вы пойдете на поводу у провайдера и воспользуетесь расширением php3, к примеру,
# то не факт, что у другого будет такое же (.phtml - как вариант).
# ну и что - мне то какая проблема, скажете Вы,
# изменить расширения у своих файлов... в итоге после запуска проекта на новом месте
# Вы с удивлением обнаружите падение посещаемости и,
# стремительно увеличивающийся /www/имя_сайта/logs/error_log
# а дело в том, что поисковые системы уже давно проиндексировали Ваш сайт,
# и ещё долго будут водить пользователей на сайт-призрак - они же не знают, что всего то и нужно,
# это изменить расширение скриптовых файлов!
# - в конце концов, это просто больше соответствует истине -
# ведь в итоге получается действительно HTML-файл.
<IfModule mod_mime.c>
AddType application/x-httpd-php .html </IfModule>
# Кстати, если нагрузка на сайт предполагается большая,
# а часть страниц можно сохранить в "компилированном" виде, т.е. в чистом HTML-е,
# то можно сэкономить немного процессорного времени "отсадив" такие страницы
# в отдельную директорию, и отключить там PHP, например так:
#<Directory /www/имя_сайта/html/article/>
# <IfModule mod_mime.c>
# RemoveType .html
# </IfModule>
#</Directory>
# или использовать для "чистых" HTML-документов другое расширение, н.п. .htm,
# но тут могут вылезти проблемы, описанные выше - вдруг эти документы
# нужно будет сделать динамическими, позже, по каким либо причинам,
# и что - менять расширение?
# очень удобно - этот файл будет исполнятся перед каждым обращением к PHP,
# в пределах данного сайта, и сюда удобно вынести присвоение переменных и define,
# а так же include связанные с загрузкой библиотек т.д. и т.п.,
# которые привязывают к физическому расположению, чего-либо.
# этот документ также находится вне DocumentRoot,
# что повышает безопасность и логично отражает иерархию
# возможно Вы скажете, что это ненужный файл - ведь все обращения итак
# обрабатываются одним файлом (н.п. index.html), и именно в нем легко можно сделать
# привязку к физическому положению! можно - но я все-таки считаю, что правильнее,
# когда в единой точке входа сосредоточена логика сайта, а физика - отдельно.
# кроме того, возможны несколько точек входа - в случае с
# виртуальными сайтами или физическими разделами (см. ниже настройку mod_rewrite)
php_admin_value auto_prepend_file "/www/имя_сайта/.startup.html" # расширение .html предлагаю использовать для единообразия картины
# Кстати, это не "стопудовый" PHP - <script language="php"></script>
# или кому больше нравится <? ?>, всё таки нужно писать...
# Для подобных же вещей (т.е. для комфортной привязки проекта,
# к конфигурации сервера) может служить вот эта директива
#php_admin_value include_path "/www/имя_сайта/include"
# она перечисляет директории, которые будут отрабатываться
# по командам серии include(). Конечно на "домашнем" и рабочем
# сервере это будут разные каталоги (если вы конечно не на одной
# платформе и там и там работаете :)
# для ошибок, происходящих в пределах PHP логичнее всего
# тоже завести отдельный файл, в пределах нашей logs директории
php_admin_flag log_errors On php_admin_value error_log "/www/имя_сайта/logs/php_errors"
# после запуска сайта в плановую эксплуатацию имеет смысл
# выключить показ сообщений о ошибках, т.к. в некоторых случаях
# в сообщениях об ошибках есть информация о конфигурации и путях сервера,
# а это прямой удар по безопасности!
php_admin_flag display_errors Off
# данный флаг управляет автоматической регистрацией в PHP всех переменных
# переданных методами GET или POST
# т.е. фактически это глобальные переменные на ряду с переменными
# определёнными в теле скрипта - вот тут и кроется "дыра" в безопасности сайта,
# ведь если злоумышленник узнает какие переменные Вы используете как внутренние,
# то он сможет управлять Вашим сайтом, через передачу нужных значений в запросах!
# второй аргумент в пользу такого решения - стиль программирования,
# на который стоит обращать внимание при сложном по структуре
# (много связанных модулей, например) проекте - обращаясь к переменной в тексте программы
# программист (имеется в виду тот, который не писал другие модули) может только догадываться
# откуда берется эта переменная: от пользователя, или из программного кода
php_admin_flag register_globals Off # конечно в небольших проектах, которые пишутся на одном дыхании,
# автоматическая регистрация может только облегчить жизнь программисту,
# и многие считают это большим плюсом, тем не менее я данный флаг ставлю
# в Off всегда и получаю значения через свои функции
# хотя бы потому, что код малого проекта может быть использован в большом,
# а там _не_использование_ автоматической регистрации (глобальных переменных то бишь)
# считаю более чем оправданным, особенно при использовании ООП
<IfModule mod_rewrite.c>
RewriteEngine On
#RewriteLog "/www/имя_сайта/logs/rewrite_log"
#RewriteLogLevel 9
# произведем обработку запроса
# уберём подряд идущие слэши
RewriteRule (.*)/(/.*) $1$2 [N]
# если какой либо элемент, название директории или имя файла, начинается с точки [.]
# то это считается служебным ресурсом и доступ к нему с клиентской стороны НЕвозможен
# кстати "так думают" ещё ls НЕ показывая по умолчанию такие файлы и samba, которая
# добавляет к таким файлам атрибут hidden
RewriteRule ^(.*/)\.(.*) %{REQUEST_FILENAME} [G]
# физические файлы и директории (в случае наличия DefaultIndex)
# имеют абсолютный приоритет
# если то, что просят, это физический файл,
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -f # то отдаем как есть и останавливаемся
RewriteRule ^ %{REQUEST_FILENAME} [L]
# это не файл - пытаемся думать, что это директория
# если не было слеша в конце, то делаем редирект
# прилепляя слеш в конец запроса - т.о. пользователь увидит "красивый" адрес
# фактически это может произойти только один раз - если запрос не файл,
# и в конце нет слеша (то, что после ? и сам ? НЕ учитывается!)
# кстати - [R,L] можно убрать,
#RewriteRule [^\/]$ %{REQUEST_FILENAME}/
# и тогда итоговый URL для пользователя будет больше походить на путь к
# виртуальному файлу, чем к виртуальной директории :) и это можно красиво использовать!
RewriteRule [^\/]$ %{REQUEST_FILENAME}/ [R,L]
# если то, что просят, это физическая директория,
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d # и в ней есть DefaultIndex файл
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}index.html -f # то передаём на исполнение этот файл
RewriteRule ^ %{REQUEST_FILENAME}index.html [L]
# если то, что просят, это физическая директория,
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d # НО в ней НЕТ DefaultIndex файл
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}index.html !-f # то передаём на исполнение сообщение об отказе в доступе
RewriteRule ^ %{REQUEST_FILENAME} [F]
# и, если запрос содержит более одной директории,
# то, зацикливаем анализ, откинув последнюю директорию
#RewriteRule (/.*/)(.*/)$ $1 [N]
# ОБРАТИТЕ ВНИМАНИЕ! Данная конструкция может добавить ненужную нагрузку на сервер,
# если Вы используете достаточно длинные (с большим количеством вложенных поддиректорий)
# URL`ы, ведь на каждый запрос она будет производить разложение на составляющие
# и анализ каждой из них, по вышеописанным правилам, поэтому,
# если вам нужны виртуальные сайты с виртуальными директориями в пределах сайта
# (что это такое - см. ниже :), то только тогда "раскоментарьте" это правило!
# а тут оседают все остальные обращения к сайту
RewriteRule ^ /index.html [L]
# Принцип работы:
# 1. Если то что просят файл - всё остаётся как есть.
# 2. Если то что просят директория, и там есть DefaultIndex файл, то управление на него.
# 3. Если дошли до этого шага? значит это и есть "виртуальная" директория -
# теперь нужно теперь найти обработчик для неё. Можно задать его жестко,
# н.п. как DefaultIndex-файл, находящийся в корне сайта,
# но можно добавить нашему ReWrite`у немного интеллекта -
# пусть он попытается подняться вверх по предполагаемому дереву каталогов,
# и там поискать, по правилу п/п 1 и 2, т.е. зациклим анализ;
# 4. Если подниматься больше некуда (или мы и находились в корне), то данную
# "виртуальную" директорию будет обрабатывать DefaultIndex-файл корня сайта.
#
# В итоге мы получим сайт с "виртуальными" директориями, но если часть структуры
# сайта построена физически, то это будет как бы сайт в сайте, т.е. если URL выглядит как
# /test/бла/бла/, и директория /test/ физическая и в ней есть DefaultIndex-файл,
# то именно он получит управление, а не корневой DefaultIndex-файл.
#
# Конечно для Ваших задач может понадобиться
# другое решение - ReWrite наверняка с ним справится! :)
</IfModule>
# Если уж тема безопасности затронута, то хотелось бы упомянуть ещё несколько моментов:
php_admin_flag safe_mode On # - safe_mode в PHP это тот режим, который практически полностью позволяет исключить
# возможность несанкционированного доступа пользователя к ресурсам доступ к которым
# (хотя бы даже и только на чтение) имеет PHP, запущенный как модуль Apache:
# 1. Запрещается изменять переменные окружения для запускаемых программ, через putenv();
# 2. Запрещается запускать программы через exec(),
# находящиеся вне указанной директории, см. safe_mode_exec_dir в php.ini;
# 3. Файловые операции корректируются:
php_admin_value open_basedir "/www/имя_сайта/" php_admin_value doc_root "/www/имя_сайта/" # ни одна из файловых функций не сможет открыть файл, если он не входит
# в указаное поддерево (их, кстати, можно указать несколько).
# Подробнее на http://www.php.net/manual/en/features.safe-mode.php
# - disable_functions данная директива позволяет отключить некоторые функции
# (конечно можно это сделать при компиляции, но "геморроя" тогда больше), которые могут
# слишком много и быстро рассказать о конфигурации сервера, например phpinfo()
# или слишком много позволить пользователю, н.п. dl()
# Кстати, данная конструкция (как и некоторые другие, safe_mode_exec_dir)
# должна устанавливаться только из php.ini, т.е. глобальна в пределах одного Apache
# - На тему загрузки модулей есть специальная директива
# php_admin_flag enable_dl Off
# хотя в safe_mode функция dl() и так не доступна
# Кстати, программисту создающему сайт на домашнем сервере имеет смысл установить
# всё настройки (в том числе и по вопросам безопасности) так, как будто этот сервер,
# доступен всем Сетевым Ветрам - просто для того, чтобы потом на реальном сервере
# не вылезла какая-либо несовместимость конфигурации
# Подробности по настройке PHP
# см. http://www.php.net/manual/en/configuration.php#ini.safe-mode
# И последнее, что хотелось бы упомянуть на этут тему: если на Вашем сервере
# размещается несколько десятков или сотен серверов, и многое в их конфигурации
# однотипно, то стоит подумать над использованием одного из механизмов, подробнее
# описанных тут http://httpd.apache.org/docs/vhosts/mass.html
# а конкретно http://httpd.apache.org/docs/mod/mod_vhost_alias.html
#VirtualDocumentRoot "/www/%0/html"
# данная директива используется как шаблон в одном <VirtualHost *></VirtualHost>
# для отработки нескольких серверов вместо DocumentRoot
# в нескольких <VirtualHost *></VirtualHost>
# где %0 полное имя сервера, т.о. для заведения одного из типичных серверов
# достаточно будет только в нужном месте создать директорию... и все!
# Есть только одна проблема - в директиве php_admin_value %0 останется неизменным :(
# Ну, что ждём от Apache Team создание virtual_php_admin_value или напишем сами?
</VirtualHost>
Яцевич Роман, неважно кто по жизни
отдельное спасибо Бохонковичу Юрию, за консультации по безопасности систем на базе Unix/Linux-серверов а также Лебедеву Дмитрию, за то, что он открыл мне глаза на отнюдь не виртуальную проблему виртуальных директорий!