Apache HTTP Server до сих пор крутит изрядную долtion сайтов и почти все панели хостинга, даже если рядом давно стоит nginx. На LPIC-2 вы должны уметь поднять сервер с нуля, развести несколько сайтов на одном IP, включить нужные модули, закрыть каталог паролем и прочитать логи, когда что-то пошло не так. В этом уроке разберём, чем отличается раскладка конфигов в Debian и RHEL, как работают виртуальные хосты по имени и по IP, зачем нужны .htaccess и AllowOverride, и как настроить базовую аутентификацию через htpasswd.

Как это работает
Главная путаница новичка - имя пакета и демона. В Debian/Ubuntu пакет и сервис называются apache2, бинарь - apache2, главный конфиг лежит в /etc/apache2/apache2.conf. В RHEL/Fedora тот же проект упакован как httpd, демон httpd, корень конфигурации - /etc/httpd/conf/httpd.conf. Это один и тот же Apache, просто дистрибутивы по-разному режут конфиг на куски.
Debian идёт по модели "много мелких файлов плюс симлинки". Реальные настройки лежат в mods-available, sites-available и conf-available, а включаются через симлинки в mods-enabled, sites-enabled, conf-enabled. Создавать и удалять эти симлинки руками не нужно - для этого есть утилиты a2enmod/a2dismod, a2ensite/a2dissite, a2enconf/a2disconf. RHEL проще: конфиги модулей лежат в conf.modules.d, дополнительные настройки и виртуалхосты - в conf.d, всё подхватывается директивами Include по маске.
Apache обрабатывает запрос так. Сначала по заголовку Host и адресу сокета он выбирает виртуальный хост. Виртуалхосты бывают двух видов. По имени (name-based) - один IP и порт, а нужный сайт выбирается по заголовку Host из HTTP-запроса; это сегодня основной режим, так живут тысячи доменов на одном адресе. По IP (IP-based) - у каждого сайта свой адрес или порт, выбор идёт ещё до чтения Host. Первый описанный VirtualHost на данном сокете становится дефолтным: он отвечает, когда Host не совпал ни с одним ServerName или ServerAlias.
Дальше Apache ищет файл. DirectoryIndex задаёт, что отдавать при запросе каталога - обычно index.html, для PHP добавляют index.php. Поведение конкретного каталога настраивается в блоке Directory. Тут важна директива AllowOverride: она решает, можно ли переопределять настройки прямо в каталоге через файл .htaccess. Если AllowOverride None (значение по умолчанию в современных сборках), .htaccess игнорируется целиком - и это правильно с точки зрения и безопасности, и скорости, потому что иначе Apache на каждый запрос читает .htaccess по всему пути. Включать .htaccess стоит только там, где вы не управляете основным конфигом, например на shared-хостинге.
Модули - это вся функциональность Apache. MPM (prefork, worker, event) определяет модель обработки; на современных системах по умолчанию event. Аутентификацию дают mod_auth_basic плюс mod_authn_file и mod_authz_*. Базовая аутентификация передаёт логин и пароль в каждом запросе закодированными base64 - то есть фактически открытым текстом, поэтому работать должна только поверх HTTPS. Пароли хранятся в отдельном файле, который создаёт утилита htpasswd, вне корня сайта.
Команды и примеры
Проверка синтаксиса и перезапуск (одинаково в обоих семействах через systemd, отличаются имена юнита):
Код: Выделить всё
# Debian/Ubuntu
apache2ctl configtest # или: apachectl -t
systemctl reload apache2
# RHEL/Fedora
apachectl configtest
systemctl reload httpd
# Список загруженных модулей и сборка MPM
apachectl -M
apachectl -V | grep -i mpm
Код: Выделить всё
# Debian/Ubuntu
a2enmod rewrite headers
a2dismod status
systemctl reload apache2
# RHEL/Fedora - модуль грузится строкой LoadModule в conf.modules.d
# проверить, что он есть:
grep -ri rewrite /etc/httpd/conf.modules.d/
Код: Выделить всё
<VirtualHost *:80>
ServerName shop.example.org
ServerAlias www.shop.example.org
DocumentRoot /var/www/shop
DirectoryIndex index.html index.php
<Directory /var/www/shop>
Require all granted
AllowOverride None
</Directory>
ErrorLog ${APACHE_LOG_DIR}/shop_error.log
CustomLog ${APACHE_LOG_DIR}/shop_access.log combined
</VirtualHost>
Код: Выделить всё
a2ensite shop.conf
apache2ctl configtest && systemctl reload apache2
Код: Выделить всё
<VirtualHost 192.0.2.10:80>
ServerName intranet.example.org
DocumentRoot /var/www/intranet
</VirtualHost>
Код: Выделить всё
# -c создаёт файл (только при первом запуске!), -B = bcrypt
htpasswd -B -c /etc/apache2/.htpasswd alice
htpasswd -B /etc/apache2/.htpasswd bob # добавить, БЕЗ -c
# в конфиге Directory или в .htaccess (если AllowOverride AuthConfig):
<Directory /var/www/shop/admin>
AuthType Basic
AuthName "Zakrytaya zona"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
</Directory>
Код: Выделить всё
# топ-10 IP по числу запросов
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head
# все ответы 500
awk '$9 ~ /^5/' /var/log/apache2/access.log
# живой просмотр ошибок (RHEL: /var/log/httpd/error_log)
tail -f /var/log/apache2/error.log
- htpasswd -c на существующем файле паролей затирает его целиком - флаг -c только для самого первого пользователя.
- .htaccess не работает, а ошибок нет - почти всегда AllowOverride None. Без нужного override (AuthConfig, Limit, FileInfo и т.д.) Apache просто игнорирует файл.
- Забыли ServerName в дефолтном хосте - Apache при старте ругается "could not reliably determine the server's fully qualified domain name", но запускается; лучше явно задать.
- Первый VirtualHost ловит все запросы с незнакомым Host. Если на сервере выскакивает "не тот" сайт - проверьте порядок и какой хост стоит первым на сокете.
- Edit конфига применился только после reload. systemctl reload перечитывает без обрыва соединений, restart рвёт их; перед любым из них делайте configtest.
- В RHEL нет a2enmod - привычка из Debian приводит к "command not found"; там модули правятся в conf.modules.d.
- Basic-аутентификация поверх голого http отдаёт пароль почти открытым текстом - обязательно за TLS.
- Поставьте Apache (apt install apache2 либо dnf install httpd) и запустите: systemctl enable --now apache2/httpd.
- Создайте каталоги /var/www/site-a и /var/www/site-b, положите в каждый свой index.html.
- Опишите два name-based виртуалхоста с ServerName a.test и b.test; в /etc/hosts пропишите их на 127.0.0.1.
- Активируйте сайты (a2ensite в Debian / Include в RHEL), сделайте configtest и reload.
- Проверьте: curl -H 'Host: a.test' http://127.0.0.1/ и то же для b.test - должны прийти разные страницы.
- Закройте каталог /var/www/site-a/secret базовой аутентификацией через htpasswd, проверьте curl с -u и без.
- Сгенерируйте 5-6 запросов и найдите их в access.log, посчитайте коды ответов через awk.
- Чем структура конфигурации Apache в Debian отличается от RHEL и какие утилиты управляют симлинками в Debian?
- По какому признаку Apache выбирает name-based и IP-based виртуалхост и какой хост отвечает при незнакомом Host?
- Что делает AllowOverride и почему значение None ускоряет работу и считается безопаснее?
- Зачем флаг -c в htpasswd опасен и в каком случае его применяют?
- Почему Basic-аутентификацию нельзя использовать без HTTPS?
- Какие директивы задают файлы логов виртуалхоста и чем combined отличается от common?