Выбор MPM модуля
MPM (Multi-Processing Module) определяет, как Apache обрабатывает параллельные соединения. От выбора MPM зависит, сколько памяти уходит на каждый запрос и насколько хорошо сервер совместим с PHP-FPM:
| MPM | Применение | Рекомендация |
|---|---|---|
| Prefork | mod_php (устаревший) | Не рекомендуется |
| Worker | Многопоточный | Если нет mod_php |
| Event | Асинхронный | Рекомендуется для PHP-FPM |
# Переключение на Event MPM
a2dismod mpm_prefork
a2enmod mpm_event
a2enmod proxy_fcgi setenvif
a2enconf php8.2-fpm
systemctl restart apache2
Prefork создаёт отдельный процесс на каждое соединение и держит его открытым до завершения запроса — при mod_php это неизбежно, но с PHP-FPM смысла в этом больше нет, а память расходуется многократно избыточно.
Реальные ошибки и предупреждения в логах
Самое частое предупреждение при нехватке воркеров под нагрузкой:
[mpm_event:error] AH00485: scoreboard is full, not at MaxRequestWorkers
[mpm_event:error] server reached MaxRequestWorkers setting, consider
raising the MaxRequestWorkers setting
Оно означает, что все доступные воркеры заняты обработкой запросов, и новые соединения встают в очередь или отклоняются. Причина обычно не в самом Apache, а в медленных PHP-скриптах, которые держат воркер дольше, чем нужно, — сначала стоит проверить slow.log, а уже потом поднимать лимит. Вторая частая ошибка возникает при неправильном подключении PHP-FPM:
[proxy_fcgi:error] AH01079: failed to make connection to backend:
httpd-UDS
Она значит, что Apache не может достучаться до сокета PHP-FPM — либо демон PHP-FPM не запущен, либо путь к сокету в конфиге проксирования не совпадает с реальным.
Оптимизация KeepAlive
# /etc/apache2/apache2.conf
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5 # Снизить с 15 до 5 секунд
KeepAliveTimeout, выставленный слишком высоко, держит воркер занятым в ожидании следующего запроса от того же клиента, даже если клиент уже закрыл вкладку. На сайтах с высокой посещаемостью это быстро съедает пул воркеров впустую.
Включение модулей оптимизации
a2enmod expires deflate headers
a2enmod http2 # HTTP/2 поддержка
Логирование медленных запросов
# В VirtualHost конфиге
CustomLog ${APACHE_LOG_DIR}/slow.log combined env=slow
SetEnvIf X-Request-Start t=([0-9]+) slow_start=$1
Как включить mod_status для диагностики
Чтобы видеть число занятых и свободных воркеров в реальном времени, включите модуль и ограничьте доступ к странице статуса только с локального адреса:
a2enmod status
# /etc/apache2/mods-enabled/status.conf
<Location "/server-status">
SetHandler server-status
Require local
</Location>
Директива Require local критична: без неё страница статуса, показывающая внутренние URL и IP-адреса клиентов, становится доступна любому посетителю извне.
Расчёт MaxRequestWorkers по памяти
Значение MaxRequestWorkers, выставленное наугад, — частая причина как ошибки «scoreboard is full», так и обратной проблемы, свопа при нехватке памяти. Правильный расчёт отталкивается от объёма RAM, а не от количества ядер CPU:
- Определите среднее потребление памяти одним процессом Apache через
ps aux | grep apache2и колонку RSS - Вычтите из общей RAM сервера память, зарезервированную под MySQL, PHP-FPM и систему
- Разделите оставшуюся память на среднее потребление одного процесса — это и есть безопасный верхний предел MaxRequestWorkers
Когда тюнинг Apache не даст результата
- Узкое место не в веб-сервере, а в самих SQL-запросах или во внешнем API — MPM и KeepAlive не ускорят то, что и так ждёт ответа базы данных
- PHP-скрипты держат воркер секундами из-за синхронных вызовов сторонних сервисов — здесь нужна очередь задач или кэширование ответа, а не настройка Apache
- Сервер физически исчерпал ресурсы CPU или памяти — повышение MaxRequestWorkers на исчерпанном железе приведёт не к росту производительности, а к своп-у и полному зависанию
- Сайт уже стоит за Nginx как reverse proxy перед Apache — часть оптимизаций (например, gzip) правильнее настраивать на уровне Nginx, а не дублировать в Apache
Проверка результата
- Проверьте, какие модули реально активны:
apachectl -M | grep mpmдолжен показать именно event, а не prefork - Прогоните нагрузочный тест до и после изменений:
ab -n 1000 -c 50 https://yourdomain.com/и сравните среднее время ответа - Понаблюдайте за
mod_status(/server-status) несколько минут под реальной нагрузкой — число занятых воркеров не должно упираться в лимит постоянно
Оптимизацию самого PHP-FPM, включая размер пула и число дочерних процессов, разбирает статья PHP-FPM: пулы, воркеры и оптимизация. Общие правила .htaccess для ускорения отдачи статики — в статье .htaccess: правила редиректов и оптимизации.