Apache vs nginx - когда что
В современном Linux два мейнстрим веб-сервера:
- Apache - process-per-request (worker/event MPM), модульный,
.htaccess, PHP через mod_php. Стандарт для shared hosting, LAMP-стека, легаси. - nginx - event-driven, легче на ресурсах, проще конфиг, лучше для static + reverse proxy.
Apache всё ещё имеет смысл когда:
- Нужны
.htaccess-овые правки силами user'ов без рестарта. - mod_*-экосистема (mod_wsgi, mod_authnz_ldap, mod_security).
- Готовый продукт ожидает Apache (ZendServer, cPanel, mailman).
В остальных случаях - nginx или Caddy.
Установка - пакетные различия
| Дистрибутив | Пакет | Демон | Конфиг | run-as |
|---|---|---|---|---|
| RHEL/Fedora | httpd | httpd | /etc/httpd/conf/httpd.conf + conf.d/ | apache |
| Debian/Ubuntu | apache2 | apache2 | /etc/apache2/apache2.conf + sites-enabled/ | www-data |
# RHEL
sudo dnf install httpd
sudo systemctl enable --now httpd
# Debian
sudo apt install apache2
sudo systemctl enable --now apache2
Дальше httpd для краткости, на Debian замени на apache2.
Структура каталогов на Debian (модель «sites-available»)
/etc/apache2/
├── apache2.conf ← главный, читает все остальные
├── ports.conf ← Listen 80, 443
├── conf-available/ ← фрагменты
├── conf-enabled/ ← симлинки на активные
├── mods-available/ ← все модули (.load + .conf)
├── mods-enabled/ ← симлинки на активные модули
├── sites-available/ ← конфиги virtual host'ов
│ ├── 000-default.conf
│ └── example.org.conf
└── sites-enabled/ ← симлинки на активные
└── example.org.conf -> ../sites-available/example.org.conf
Активация через утилиты:
sudo a2ensite example.org # symlink в sites-enabled
sudo a2dissite example.org # снять
sudo a2enmod ssl rewrite # включить модули
sudo a2dismod autoindex
sudo systemctl reload apache2
RHEL такого разделения нет - все конфиги дропаются в /etc/httpd/conf.d/*.conf,
модули загружаются через LoadModule в /etc/httpd/conf.modules.d/*.conf.
Process model - почему root и www-data одновременно
Apache стартует от root чтобы открыть [[capabilities|порты <1024]]
(80, 443 требуют CAP_NET_BIND_SERVICE), потом fork'ает worker'ов как
unprivileged user (www-data или apache).
ps -ef | grep httpd
# root 1234 1 0 /usr/sbin/httpd -DFOREGROUND ← parent (master)
# apache 1235 1234 0 /usr/sbin/httpd -DFOREGROUND ← worker
# apache 1236 1234 0 /usr/sbin/httpd -DFOREGROUND ← worker
Worker'ы обрабатывают запросы. Если в них уязвимость - exploit имеет
только права apache, не root. Поэтому /var/www/html/ должна быть
читаемая apache, но не writable (иначе RCE → запись на диск).
VirtualHost - несколько сайтов на одном IP
Один хост может обслуживать несколько доменов на одном IP по Host:
заголовку из HTTP/1.1.
# /etc/apache2/sites-available/example.org.conf
<VirtualHost *:80>
ServerName example.org
ServerAlias www.example.org
DocumentRoot /var/www/example.org
ErrorLog ${APACHE_LOG_DIR}/example.org-error.log CustomLog ${APACHE_LOG_DIR}/example.org-access.log combined<Directory /var/www/example.org>
Options -Indexes +FollowSymLinks
AllowOverride All # разрешить .htaccess
Require all granted
</Directory>
</VirtualHost>
Подвохи:
- Первый VirtualHost = default: запросы с неизвестным
Host:падают в него. Часто хочется default-конфиг с 444 ответом для защиты от «спам-запросов на IP». *:80vsIP:80-*ловит все интерфейсы; явный IP - только этот.<Directory>vs<Location>vs<Files>- каталог на диске vs URL-путь vs паттерн файла. Путать опасно: правила контроля доступа могут не сработать.
HTTPS - mod_ssl + Let's Encrypt
# 1. Включить модуль
sudo a2enmod ssl
sudo systemctl reload apache2
# 2. Получить сертификат (certbot пропишет конфиг сам)
sudo apt install certbot python3-certbot-apache
sudo certbot --apache -d example.org -d www.example.org
# certbot создаст:
# /etc/apache2/sites-available/example.org-le-ssl.conf
Минимальный SSL-VirtualHost вручную:
<VirtualHost *:443>
ServerName example.org
DocumentRoot /var/www/example.org
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.org/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.org/privkey.pem
# Современные шифры
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!aNULL:!MD5
SSLHonorCipherOrder on
</VirtualHost>
Про сам tls-handshake - отдельная статья.
Reverse proxy для backend-приложения
<VirtualHost *:80>
ServerName api.example.org
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:8000/
ProxyPassReverse / http://127.0.0.1:8000/
RequestHeader set X-Forwarded-Proto "http"
</VirtualHost>
Нужны модули proxy и proxy_http:
sudo a2enmod proxy proxy_http
Backend (uvicorn/gunicorn/etc.) слушает loopback:8000, Apache на 80/443 отдаёт TLS, заголовки и rate-limit'ы.
.htaccess - правила per-каталог без перезагрузки
Основная фича Apache - файл .htaccess в любом каталоге может содержать
директивы конфига и применяется БЕЗ рестарта сервера. Удобно для
shared hosting'а где user-у дают только FTP-доступ.
# /var/www/example.org/.htaccess
Options -Indexes
ErrorDocument 404 /404.html
RewriteEngine On
RewriteRule ^old-page$ /new-page [R=301,L]
Чтобы это работало - в VirtualHost должно стоять AllowOverride All
для каталога. По умолчанию - None (.htaccess игнорируется).
Цена: Apache читает .htaccess на каждый запрос в этой иерархии. На high-load - выключай и переноси правила в основной конфиг.
Управление
sudo systemctl reload httpd # перечитать конфиг без обрыва соединений
sudo systemctl restart httpd # полный рестарт
sudo apachectl configtest # проверка синтаксиса (== httpd -t)
sudo apachectl -S # показать все VirtualHost'ы и их правила
sudo apachectl -M # список загруженных модулей
Всегда configtest перед reload. Иначе syntax error → демон не
перезапустится, и сайт ляжет.
Логи
RHEL: /var/log/httpd/{access,error}_log.
Debian: /var/log/apache2/{access,error}.log.
sudo tail -f /var/log/apache2/error.log # ошибки в реалтайме
sudo tail -f /var/log/apache2/access.log # запросы
Формат combined (default):
192.168.0.5 - - [29/Apr/2026:14:23:11 +0300] "GET / HTTP/1.1" 200 1234 "-" "Mozilla/5.0..."
↑ ↑ ↑ ↑
client request code size
Logrotate конфиг обычно /etc/logrotate.d/httpd ротирует daily/weekly.
Безопасность - обязательный минимум
- Не запускать как root postfork: убедись
User apache/User www-dataстоит в конфиге. - Скрыть версию:
ServerTokens Prod+ServerSignature Offвapache2.conf. - Отключить листинг каталогов:
Options -Indexesдля всех<Directory>. - Запретить .git/.env:
<DirectoryMatch "/\\.git">+Require all denied. - HSTS:
Header always set Strict-Transport-Security "max-age=63072000". - fail2ban на лог error.log - банить тех кто долбит 404 и 401.
- mod_security + OWASP ruleset - WAF для типичных атак.
Дебаг - типичные проблемы
# 1. Запущен?
systemctl status httpd
ss -tlnp 'sport = :80' # слушает порт?
# 2. Куда идут логи в реалтайме
sudo tail -F /var/log/httpd/error_log
# 3. Какой VirtualHost матчится для имени
sudo apachectl -S | grep example.org
# 4. Простой curl-тест
curl -I http://localhost
curl -kvI https://example.org
Частые ошибки:
- 403 Forbidden на чистом сайте → SELinux на RHEL. Проверить
ls -Z /var/www/html- нужен контекстhttpd_sys_content_t.sudo restorecon -R /var/www/html. - Permission denied в логе →
apache/www-dataне может прочитать DocumentRoot.chmod o+rX -Rили нужный ACL. - Address already in use при старте → кто-то другой уже на 80
(
ss -tlnp 'sport = :80'найдёт).