Перейти к содержанию

Техническая глубина

Укрепление Apache против DDoS: выбор MPM, mod_reqtimeout и лимиты по источнику

Обновлено: август 2026 г. · Выбор MPM, mod_reqtimeout, mod_qos и mod_evasive · Время чтения ~20 мин

Мастерская, где каждая входящая задача занимает целый верстак с рабочим; поток янтарных задач оставляет все верстаки занятыми и простаивающими, а во второй комнате общая быстрая линия продолжает двигаться.

Первое решение по DDoS в Apache — это MPM. prefork тратит процесс на каждое соединение и уязвим к Slowloris; event не тратит и куда устойчивее. Поверх верного MPM mod_reqtimeout закрывает атаки медленных запросов, mod_qos ограничивает соединения на источник, mod_evasive блокирует потоки запросов. Каждая — это директива со счётчиком, настроенная под ваши базовые показатели.

Это руководство укрепляет сам Apache httpd против той части DDoS-атаки, что исчерпывает соединения и запросы, без устройства перед ним. В отличие от nginx устойчивость Apache не даётся даром. Её определяет сначала выбор, который большинство руководств проскакивает: модуль многопроцессорной обработки, MPM. Ошибётесь здесь — и настройка модулей ниже вас не спасёт. Выберете верно — и модули делают своё дело.

Каждая директива идёт со своим модулем, значением и счётчиком, показывающим её работу. Значения — это отправные точки относительно вашего трафика.

Формы атак и отвечающие им механизмы Apache
УстройствоФорма атакиМеханизм ApacheПроверка
Медленный заголовок / медленное тело (Slowloris)mod_reqtimeout RequestReadTimeout; event MPMдоля 408 в журнале доступа; табло в server-status
Много соединений с источникаmod_qos QS_SrvMaxConnPerIPзанятые рабочие в server-status; консоль mod_qos
Поток запросов с немногих источниковmod_evasive DOSPageCount / DOSSiteCountдоля 403; журнал/почтовый хук mod_evasive
Исчерпание рабочихMaxRequestWorkers, ThreadsPerChild (event MPM)server-status: занятые и свободные рабочие
Злоупотребление крупным запросомLimitRequestBody, LimitRequestFields, LimitRequestLineдоля 413 / 400 в журнале доступа

Первая строка определяет остальное. На prefork Slowloris исчерпывает процессы прежде, чем сработает любой другой лимит, поэтому выбор MPM идёт раньше настройки модулей. Как только атака заполняет канал перед Apache, ничего из этого не помогает.

0. Сначала базовые показатели: включите табло

Живое состояние Apache — это табло mod_status. Включите его с ограничением на локальный хост и активируйте расширенный статус.

ExtendedStatus On
<Location "/server-status">
    SetHandler server-status
    Require ip 127.0.0.1
</Location>
# Табло рабочих: количество по состояниям
curl -s http://127.0.0.1/server-status?auto | grep -E 'BusyWorkers|IdleWorkers|Total Accesses'

# Число запросов на клиента из журнала доступа, обычная неделя
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head

BusyWorkers относительно предела MaxRequestWorkers и частота на клиента из журнала — это базовые показатели, относительно которых измеряется каждый порог ниже.

1. MPM: решение, идущее прежде всего

Именно этот раздел отделяет устойчивый Apache от уязвимого. Посмотрите, какой MPM активен.

apachectl -V | grep -i 'Server MPM'
# или
apache2ctl -M | grep mpm

Если написано prefork, это первое, что нужно менять, если только потоконебезопасный модуль не вынуждает обратное. prefork запускает один процесс на соединение, поэтому атака Slowloris исчерпывает пул процессов несколькими тысячами медленных соединений. event использует потоки и отдельный слушатель, который разгружает keep-alive и завершающиеся соединения, так что медленное соединение стоит слота потока, а не процесса.

Обычная причина, по которой сервер застрял на prefork, — это mod_php. Перенос PHP на PHP-FPM освобождает вас для запуска event и даёт здесь наибольший выигрыш в устойчивости.

# настройка mpm_event — общая ёмкость = ServerLimit * ThreadsPerChild
<IfModule mpm_event_module>
    StartServers            4
    ServerLimit             16
    ThreadsPerChild         64
    MaxRequestWorkers       1024      # ServerLimit * ThreadsPerChild
    MaxConnectionsPerChild  10000     # переработка для ограничения роста памяти
</IfModule>
# Подтвердите работающий MPM и предел
apachectl -V | grep -i mpm
curl -s http://127.0.0.1/server-status?auto | grep -E 'BusyWorkers|IdleWorkers'

MaxRequestWorkers — жёсткий предел одновременных запросов. Задайте его на уровне, который держит ваша оперативная память, не выше, потому что превышение памяти под нагрузкой — это отдельный отказ.

2. mod_reqtimeout: прямой ответ Slowloris

mod_reqtimeout ограничивает время, за которое клиент вправе отправить запрос. Ступенчатая форма даёт щедрое начальное окно, сужающееся по мере прихода байтов, и отличает медленного, но настоящего клиента от атакующего, шлющего по одному байту.

<IfModule mod_reqtimeout_module>
    # заголовок: 20с старт, растёт до 40с при минимуме 500 байт/с
    # тело:      20с, растёт при минимуме 500 байт/с
    RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500
</IfModule>
# Сброшенные медленные запросы видны в журнале доступа как 408
awk '$9==408' /var/log/apache2/access.log | wc -l

Растущая доля 408 во время события — это таймаут за работой. Если в обычном трафике в 408 начинают появляться настоящие мобильные клиенты, окно заголовка слишком узкое. Расширьте максимум и MinRate вместе.

3. mod_qos: соединения на источник и в целом

mod_qos — это уровень одновременности соединений и справедливости. Ближайший аналог nginx-овой limit_conn в Apache.

<IfModule mod_qos_module>
    QS_SrvMaxConn            2048     # всего одновременных соединений
    QS_SrvMaxConnPerIP       50       # на адрес источника
    QS_SrvMaxConnClose       70%      # выше этой нагрузки отключить keep-alive, освобождая слоты
    QS_SrvMinDataRate        150 1200 # мин. байт/с, растёт под нагрузкой — второй срез Slowloris
</IfModule>

QS_SrvMaxConnClose тонкая и полезная. Выше заданной доли пула Apache перестаёт соблюдать keep-alive, так что соединения освобождаются быстрее. QS_SrvMinDataRate навязывает минимальную пропускную способность, растущую по мере заполнения сервера, и отсекает самые медленные соединения именно тогда, когда слотов мало.

# mod_qos выводит собственные счётчики
curl -s 'http://127.0.0.1/server-status?auto' | grep -i qos

4. mod_evasive: блок потока запросов

mod_evasive за короткое окно считает запросы на источник и временно блокирует источник, перешедший порог.

<IfModule mod_evasive20_module>
    DOSHashTableSize    3097
    DOSPageCount        5        # одна страница, на источник, за интервал
    DOSSiteCount        50       # любая страница сайта, на источник, за интервал
    DOSPageInterval     1        # секунды
    DOSSiteInterval     1
    DOSBlockingPeriod   30       # длительность блокировки, секунды
    DOSLogDir           /var/log/apache2/evasive
</IfModule>
# Заблокированные источники пишутся в журнал и получают 403
awk '$9==403' /var/log/apache2/access.log | wc -l
ls /var/log/apache2/evasive/    # по одному файлу-замку на каждый сейчас заблокированный источник

Держите DOSBlockingPeriod поначалу коротким. Долгая блокировка плюс ложное срабатывание запирает настоящего пользователя на всю её длительность. Как и с любым лимитом здесь, наблюдайте, что он ловит, прежде чем ему доверять.

5. Лимиты размера и полей запроса

Закройте поверхность крупных запросов, способную исчерпать память независимо от числа соединений.

LimitRequestBody      10485760    # 10 MB
LimitRequestFields    100         # макс. число заголовков
LimitRequestFieldSize 8190        # макс. размер заголовка
LimitRequestLine      8190        # макс. длина строки запроса
Timeout               60          # общий таймаут ввода-вывода, ниже стандартных 300
KeepAliveTimeout      5           # короткий простой keep-alive
MaxKeepAliveRequests  100
# Крупные запросы: 413 (тело) и 400 (заголовок/строка)
awk '$9==413 || $9==400' /var/log/apache2/access.log | wc -l

Стандартное значение общего Timeout в 300 секунд слишком щедро для публичного сервера. Его снижение само по себе — тихое, но настоящее смягчение Slowloris.

6. Верно определить IP клиента за прокси

Каждый лимит на источник, то есть mod_qos и mod_evasive, привязан к адресу клиента. За прокси это адрес самого прокси, пока он не исправлен через mod_remoteip.

<IfModule mod_remoteip_module>
    RemoteIPHeader X-Forwarded-For
    RemoteIPTrustedProxy 10.0.0.0/8 172.16.0.0/12
</IfModule>
# Записывать настоящего клиента, а не прокси
LogFormat "%a %l %u %t \"%r\" %>s %b" combined_realip
# Если вернёт 1, каждый запрос приписан прокси, и лимиты на источник бессмысленны
awk '{print $1}' /var/log/apache2/access.log | sort -u | wc -l

Сигналы для мониторинга

# 1. Занятые рабочие / предел — насыщение пула
curl -s http://127.0.0.1/server-status?auto | awk -F': ' '/BusyWorkers/{print $2}'
# 2. Доля 408 — срабатывание таймаутов медленных запросов
awk '$9==408' /var/log/apache2/access.log | wc -l
# 3. Доля 403 — блокировки mod_evasive
awk '$9==403' /var/log/apache2/access.log | wc -l
# 4. Стена R на табло — подпись Slowloris
curl -s http://127.0.0.1/server-status | grep -o 'R' | wc -l

Стена рабочих в состоянии R (чтение) — безошибочная подпись Slowloris. Рабочие, насыщенные в W (запись), — это поток запросов. На табло они выглядят по-разному и требуют разных лимитов.

Честный предел укрепления Apache

Всё здесь защищает от исчерпания состояния соединений и скорости запросов на краю приложения. Два случая лежат вне этого.

Первый — заполнение канала. Объём, забивающий канал, вообще не доходит до Apache, и никакой MPM или модуль этого не меняет.

Граница, за которой уровень на площадке уже не помогает Ваш канал доступа 10 Гбит/с Канал уже забит — только вышестоящий уровень Устройство на площадке подавляет 2 Гбит/с 8 Гбит/с 25 Гбит/с 120 Гбит/с 1 Тбит/с+ Объём атаки (логарифмическая шкала)
Ниже канала у лимитов Apache есть доля; выше него ни одна директива ничего не значит.

Второй — уровень ядра под Apache: очереди SYN, conntrack, дескрипторы. Запрос проходит его прежде, чем httpd его увидит, и его разбирает руководство по укреплению Linux. Укрепление Apache исходит из того, что этот уровень уже на месте. Выше канала ответ — сетевой уровень; Apache держит край приложения до тех пор.

Порядок применения

  1. Включите mod_status; запишите базовые показатели за неделю.
  2. Проверьте MPM. Если prefork, перенесите PHP на PHP-FPM и перейдите на event — прежде всего.
  3. Задайте ёмкость event MPM на уровне, который держит память.
  4. Добавьте mod_reqtimeout; следите за долей 408.
  5. Если есть прокси, исправьте mod_remoteip прежде лимитов на источник.
  6. Добавьте mod_qos и mod_evasive; держите периоды блокировки короткими, пока не станете доверять.
  7. Заведите четыре сигнала в мониторинг.

Второй шаг не опционален и не переставим. На prefork каждый лимит под ним применяется к серверу, который Slowloris исчерпывает в любом случае.

Частые вопросы

Какой MPM запускать ради устойчивости к DDoS?
Почти во всех случаях event. prefork запускает один процесс на соединение, поэтому несколько тысяч медленных соединений исчерпывают пул процессов. Это и есть классический результат Slowloris. prefork существует в основном ради потоконебезопасных модулей вроде старого mod_php. event и worker используют потоки и отдельный слушатель, который принимает keep-alive и завершающиеся соединения, так что медленное соединение стоит слота, а не целого процесса. Если вы на prefork только из-за mod_php, перенос PHP на PHP-FPM позволяет перейти на event и даёт здесь наибольший выигрыш в устойчивости.
Достаточно ли одного mod_reqtimeout против Slowloris?
На event почти достаточно; на prefork он помогает, но MPM всё равно вас ограничивает. RequestReadTimeout ограничивает время, за которое клиент обязан отправить строку запроса и заголовки, поэтому соединение, цедящее заголовки по байту, сбрасывается по таймауту, а не держится открытым. Это прямой ответ Slowloris. Но на prefork каждое удержанное соединение до срабатывания таймаута остаётся целым процессом, так что таймаут должен быть коротким, а пул процессов всё равно потолок. На event тот же таймаут куда действеннее, потому что цена застрявшего соединения изначально мала.
mod_evasive или mod_qos — что нужно?
Они решают разные формы, и многие сайты запускают оба. mod_evasive за короткое окно считает запросы к странице и к сайту на источник и временно блокирует источник, перешедший порог. Это защита от потока запросов. mod_qos ограничивает одновременные соединения на источник и в целом и может приоритизировать заведомо хороший трафик под нагрузкой. Это защита по одновременности соединений и справедливости. mod_evasive быстрее развернуть, mod_qos мощнее и требует больше настройки. Ни один не заменяет верный MPM и mod_reqtimeout под ними.
Какие значения RequestReadTimeout задавать?
Начните с таймаута заголовка в низкие десятки секунд, сужающегося по мере прихода байтов, и более короткого таймаута тела, затем читайте долю 408. Ступенчатая форма — более щедрое начальное окно, сужающееся с приходом данных, — отличает медленного, но настоящего клиента на плохом канале от клиента Slowloris, шлющего по одному байту. Слишком жёстко — отрежете мобильных на плохой связи; слишком мягко — медленные атаки выживут. Доля 408 относительно ваших базовых показателей подскажет, куда двигаться.
Как наблюдать за Apache во время атаки?
Через mod_status с включённым ExtendedStatus. Табло показывает состояние каждого рабочего: чтение (R), отправка (W), keep-alive (K), закрытие (C). Атака Slowloris выглядит как стена рабочих, застрявших в R, а поток запросов — как рабочие, насыщенные в W. Ограничьте server-status локальным хостом, следите за числом занятых против свободных и заведите число занятых относительно MaxRequestWorkers в мониторинг. Когда занятые приближаются к пределу, узкое место — пул.
Останавливают ли эти настройки объёмную атаку?
Нет. Если атака заполняет канал перед сервером, Apache вообще не получает запросы и никакая настройка MPM или модуля не применяется. Всё здесь защищает от исчерпания состояния соединений и скорости запросов на краю приложения. Объём выше пропускной способности канала — это задача сетевого уровня, и она не решается в httpd.conf.

Опубликовано: август 2026 г.

Руководство обновляется по мере выхода новых моделей и условий лицензирования. Как мы сравниваем производителей