В продакшене веб‑сервис держится на мелочах: от «пустяковых» таймаутов зависит, увидит ли пользователь контент или утонет в 408/504 и бесконечных ретраях. В Apache набор ключевых директив — TimeOut, KeepAliveTimeout, RequestReadTimeout (mod_reqtimeout) и ProxyTimeout — управляет жизненным циклом соединений с клиентом и бэкендом. Разберёмся, как они действительно работают, какие значения выбирать под разные сценарии и как избежать ловушек, которые выливаются в простои и утечки ресурсов.
Карта таймаутов Apache 2.4: кто за что отвечает
Чтобы успешно настраивать таймауты, важно понимать зону ответственности каждой директивы:
TimeOut— общий сетевой таймаут (по умолчанию 60 секунд): ожидание завершаемых операций ввода/вывода на клиентском соединении и прокси‑бэкенде, если не переопределено более узкими директивами.KeepAliveиKeepAliveTimeout— политика постоянных соединений HTTP/1.x и ожидание следующего запроса в рамках уже установленного соединения.RequestReadTimeout(модульmod_reqtimeout) — гибкий контроль скорости/тайминга чтения заголовков и тела запроса; основная защита от Slowloris/slow POST.ProxyTimeoutи параметрыProxyPass ... timeout= / connectiontimeout=— таймауты при взаимодействии с upstream (mod_proxy_*), в том числе reverse proxy к приложению или другому веб‑серверу.
Правило большого пальца:
TimeOut— «сетевой потолок по умолчанию», от которого вы сознательно «откусываете» поведение более конкретными настройками в нужных местах.
TimeOut: базовый предел ожидания IO
TimeOut — это не «таймаут всего подряд», но именно он определяет, как долго Apache готов ждать завершения блокирующих операций сети там, где нет отдельных переопределений. По умолчанию — 60 секунд. Слишком маленькое значение приведёт к обрывам медленных загрузок или нестабильных сетей, слишком большое — к удержанию воркеров, что под высоким трафиком проявится в утилизации CPU/памяти и очередях.
Где TimeOut влияет в первую очередь:
- Чтение клиентского запроса (если не включён/не настроен
RequestReadTimeout). - Ожидание отклика от бэкенда прокси (если не задан
ProxyTimeout/ProxyPass timeout=). - Передача ответа клиенту, если клиентская сторона «тормозит».
Рекомендованные ориентиры для TimeOut
- Публичные сайты/лендинги: 30–60 секунд (обычно 30–45 достаточно).
- API с чёткими SLO: 15–30 секунд (чтобы не «поддерживать» слишком долгие бэкенд‑операции).
- Тяжёлые загрузки файлов напрямую в Apache: 120–300 секунд, в паре с корректно настроенным
RequestReadTimeout(по скорости).
Пример (глобальный минимум):
# /etc/apache2/apache2.conf или httpd.conf
TimeOut 30Снижать TimeOut ниже 15 секунд без веских причин рискованно: накроете легитимных пользователей с медленным каналом и мобильных клиентов под нагрузкой сети.
KeepAlive и KeepAliveTimeout: экономим рукопожатия, но не держим сеть вхолостую
Постоянные соединения (HTTP/1.1) сокращают латентность за счёт переиспользования TCP и TLS, уменьшая handshake‑издержки. При этом «висящие» keep‑alive‑коннекты занимают дескрипторы и память. Баланс ищется значениями KeepAliveTimeout и лимитом MaxKeepAliveRequests.
KeepAliveOn — почти всегда верное решение под статический/смешанный контент.KeepAliveTimeout— сколько ждать следующий запрос по уже установленному соединению. По умолчанию 5 секунд (Apache 2.4).MaxKeepAliveRequests— сколько запросов разрешено в одном соединении (по умолчанию 100).
Под MPM event цена «висящих» соединений ниже, чем в worker/prefork, но всё равно не нулевая. На загруженных сайтах обычно достаточно 2–5 секунд; для SPA/рестов — 5–10 секунд; для долгоживущих соединений типа SSE/комет лучше использовать специализированные настройки и отдельные бэкенды, а не пытаться «растянуть» KeepAliveTimeout.
# Глобально
KeepAlive On
KeepAliveTimeout 5
MaxKeepAliveRequests 100Важно: KeepAliveTimeout относится к ожиданию следующего запроса в HTTP/1.x. Для HTTP/2 (mod_http2) действуют иные лимиты на сессию/потоки, а не KeepAliveTimeout впрямую. Если используете HTTP/2, проверьте соответствующие параметры модуля для тонкой настройки сессий.

RequestReadTimeout: точечная защита от Slowloris и «медленных тел»
mod_reqtimeout добавляет умную логику чтения запросов: таймауты на заголовки и тело учитывают скорость поступления данных. Он не столько «жёстко рубит» по времени, сколько «просит» клиента поддерживать минимальную скорость или завершить передачу в разумные сроки.
Синтаксис (упрощённо):
RequestReadTimeout header=initial-timeout[-max-timeout][,MinRate=bytes_per_sec]RequestReadTimeout body=initial-timeout[-max-timeout][,MinRate=bytes_per_sec]- Опционально
env=для включения/исключения правила по переменной окружения запроса.
Базовый безопасный профиль для сайтов и API
# Включаем модуль (Debian/Ubuntu)
a2enmod reqtimeout
# /etc/apache2/conf-available/reqtimeout.conf или в VirtualHost
RequestReadTimeout header=20-40,MinRate=500
RequestReadTimeout body=10,MinRate=1000Этот профиль говорит: заголовки должны прийти за 20 секунд, но допускается продление до 40 при непрерывной передаче; тело — минимум 1 КБ/с после стартового окна. Значения подбирайте под реальный профиль клиентов (мобайл, развёрнутые по всему миру). Если используете CDN/прокси перед Apache — учтите их буферизацию: Apache видит уже непрерывный поток, и можно быть строже. Кстати, не забудьте про базовые заголовки защиты; подробности в гайде по HTTP Security Headers для Nginx/Apache.
Исключения для WebSocket и долгих аплоадов
WebSocket‑соединения не подходят под логику RequestReadTimeout; их лучше исключить по заголовку Upgrade:
# Исключаем WebSocket из правил (в VirtualHost / Directory)
SetEnvIfNoCase Upgrade websocket is_websocket=1
RequestReadTimeout header=20-40,MinRate=500 env=!is_websocket
RequestReadTimeout body=10,MinRate=1000 env=!is_websocketДля страниц с большими загрузками (например, до 2 ГБ) разумно ставить минимальную скорость ниже и большее стартовое окно, но не превращать сервер в «бесконечный» буфер. Контролируйте лимиты на стороне приложения и используйте возобновляемые аплоады, если возможно.
ProxyTimeout и таймауты прокси: upstream не должен «висеть»
Если Apache работает как reverse proxy (mod_proxy_http, mod_proxy_fcgi, mod_proxy_uwsgi, mod_proxy_wstunnel и др.), отдельные таймауты на бэкенд критичны. По умолчанию, когда ProxyTimeout не задан, используется значение TimeOut. Это редко оптимально: для API часто нужен более короткий предел, чем для клиентских загрузок.
ProxyTimeout— общий таймаут для взаимодействия с бэкендом (чтение/запись), если не переопределено.- Параметры
ProxyPass:connectiontimeout=(установление TCP к бэкенду),timeout=(ожидание ответа/IO),acquire=(ожидание свободного воркера/соединения), и др. Они перекрываютProxyTimeoutадресно.
Примеры: HTTP‑бэкенд, FCGI и WebSocket
# Глобальный дефолт на прокси короче, чем общий TimeOut
TimeOut 45
ProxyTimeout 20
# Reverse proxy к приложению HTTP
ProxyPass /api http://127.0.0.1:8080/ retry=0 connectiontimeout=2 timeout=15 acquire=3000
ProxyPassReverse /api http://127.0.0.1:8080/
# FCGI к PHP-FPM (mod_proxy_fcgi)
ProxyPassMatch ^/(.*) fcgi://127.0.0.1:9000/var/www/public/$1 timeout=30
# WebSocket (тоннель без агрессивных таймаутов)
ProxyPass /ws ws://127.0.0.1:3000/ timeout=0
ProxyPassReverse /ws ws://127.0.0.1:3000/Пояснения:
retry=0— не пытаться использовать «плохой» бэкенд повторно, если он помечен как down.timeout=0вmod_proxy_wstunnelпозволяет долгоживущим WebSocket‑соединениям жить без искусственного обрыва.- Для FCGI лучше задавать явный
timeout=, чтобы зависшие скрипты не удерживали воркеры бесконечно.
Зоны видимости директив: где что можно задавать
TimeOut,KeepAliveTimeout— только в серверном/глобальном контексте, не в .htaccess.RequestReadTimeout— серверный/виртуальный хост/контекст каталога. Можно в .htaccess, но лучше централизованно.ProxyTimeout— глобально или в виртуальном хосте; параметрыProxyPass— на конкретные локации/маршруты.
Практические пресеты
1) Публичный сайт с кэшем и смешанным контентом
# Глобально
TimeOut 30
KeepAlive On
KeepAliveTimeout 5
MaxKeepAliveRequests 100
# Защита от медленных клиентов
RequestReadTimeout header=20-40,MinRate=500
RequestReadTimeout body=10,MinRate=1000
# Если есть reverse proxy к приложению (например, для /api)
ProxyTimeout 15
ProxyPass /api http://127.0.0.1:8080/ connectiontimeout=2 timeout=12 retry=0
ProxyPassReverse /api http://127.0.0.1:8080/2) API с жёстким SLO (p95 < 1–2 c, p99 < 10 c)
TimeOut 20
KeepAlive On
KeepAliveTimeout 3
MaxKeepAliveRequests 100
RequestReadTimeout header=10-20,MinRate=1000
RequestReadTimeout body=10,MinRate=2000
ProxyTimeout 10
ProxyPass /v1 http://127.0.0.1:9000/ connectiontimeout=1 timeout=8 acquire=2000 retry=0
ProxyPassReverse /v1 http://127.0.0.1:9000/Смысл — не давать «висящим» бэкендам съедать пул соединений; ошибки лучше быстро и предсказуемо возвращать клиентам с ретраями по экспоненте.
3) Большие загрузки (несколько гигабайт) напрямую в Apache
TimeOut 180
KeepAlive On
KeepAliveTimeout 10
MaxKeepAliveRequests 200
# Позволяем медленной, но поступательной передаче тела
RequestReadTimeout header=20-60,MinRate=500
RequestReadTimeout body=20,MinRate=500
# Если проксируем загрузки в приложение — учитывайте его лимиты
ProxyTimeout 120
ProxyPass /upload http://127.0.0.1:8081/ connectiontimeout=2 timeout=120 retry=0
ProxyPassReverse /upload http://127.0.0.1:8081/Не забудьте согласовать лимиты в приложении и в веб‑сервере: размеры тела, буферы, лимиты времени выполнения и число воркеров. Иначе «узкое место» проявится не там, где ожидаете. Для полного стека кеширования и валидаторов подсмотрите приёмы в гайде по Cache-Control и ETag.
Диагностика: как понять, кто виноват — клиент, Apache или бэкенд
Подход «сверху вниз»: метрики, логи, затем точечные проверки.
- Включите
mod_statusи наблюдайте состояние воркеров: «Reading Request», «Sending Reply», «Keepalive», «DNS Lookup» — это поможет локализовать фазу зависаний. - Добавьте время обработки в формат доступа:
%D(микросекунды) и%T(секунды). Если проксируете — логируйте upstream‑тайминги черезmod_proxyи маркеры результата.
# Пример расширенного лога
LogFormat "%h %l %u %t \"%r\" %>s %b %D microsec %T sec \"%{Referer}i\" \"%{User-Agent}i\"" combined_time
CustomLog logs/access_time.log combined_time- Проверяйте ретраи клиента:
curl -vпокажет, где происходит обрыв (на чтении заголовков, теле или при ожидании ответа). - Смотрите на распределение времени: если
%Dвелик, но бэкенд по своим метрикам быстр — вероятно, клиент «не принимает» ответ, иTimeOutслишком велик для отправки. - При 408/504 сравните текущие значения
TimeOut,ProxyTimeout,RequestReadTimeoutс фактическими длительностями запросов.

Частые ошибки и анти‑паттерны
- Ставят огромный
KeepAliveTimeout«на всякий случай». Итог — тысячи висящих соединений, рост дескрипторов и памяти, падение пропускной способности под пиками. - Отключают
RequestReadTimeoutна публичном фронте. Получают уязвимость к Slowloris/slow POST под малым трафиком ботов. - Забывают
ProxyTimeoutи удивляются 60‑секундным «плато» задержек: по умолчанию используетсяTimeOut, что для API почти всегда слишком много. - Слишком низкие таймауты в регионах с высоким RTT и перегрузкой радиосети: бьют легальных мобильных пользователей. Всегда проверяйте метрики RTT/трэйс и профили клиентов.
- Смешивание длинных и коротких запросов под один пул без разделения. Решение: разнести локации/виртуальные хосты и дать разным маршрутам собственные таймауты и пулы воркеров. На нагруженных проектах удобно вынести тяжёлые маршруты на отдельный VDS.
Ресурсная модель и оценка влияния
Под MPM event постоянные соединения обрабатываются эффективнее, но всё равно занимают память на контекст соединения и сокет. Прикидывайте верхнюю границу: одновременные соединения × средний объём на соединение плюс буферы ответа. Слишком большие KeepAliveTimeout и TimeOut увеличивают хвост распределений по времени жизни соединений, что при всплесках легко приводит к исчерпанию дескрипторов и росту задержек GC в приложениях за прокси.
Практика: держите короткие дефолты и адресно увеличивайте таймауты только там, где обязательно (загрузки, WebSocket, отчёты), изолируя такие маршруты в конфигурации. Если вы обдумываете архитектурные компромиссы и выбор фронта, загляните в сравнение Nginx vs Apache.
Чек‑лист внедрения
- Определите профили трафика: статический сайт, API, загрузки, WebSocket.
- Поставьте разумные глобальные дефолты:
TimeOut20–45,KeepAliveTimeout3–7,MaxKeepAliveRequests100–200. - Включите и настройте
RequestReadTimeout; исключите WebSocket черезenv. - Для прокси: задайте
ProxyTimeoutи адресные параметрыProxyPass ... connectiontimeout= timeout=. - Разнесите длинные/короткие запросы по разным локациям/виртуальным хостам с разными таймаутами.
- Добавьте метрики/логи времени обработки (
%D,%T) и наблюдайте p95/p99 до и после изменений. - Проведите нагрузочные прогоны с эмуляцией медленных клиентов и высоких RTT.
- Документируйте принятые значения и бизнес‑требования (макс. время ответа, допустимая скорость загрузки).
FAQ: тонкости и пограничные случаи
- Влияет ли
TimeOutна TLS‑рукопожатие? TLS имеет собственные таймауты вmod_ssl(например,SSLHandshakeTimeout). Но для простоты держите общийTimeOutне экстремально низким и проверяйте настройкиmod_ssl, если видите обрывы на handshake под высокой нагрузкой. Если у вас нет корректного TLS — начните с надёжных SSL-сертификаты. - Что ставить для долгих SSE/стриминга? Стриминг лучше изолировать на отдельный маршрут/виртуальный хост с осознанными таймаутами или специализированной инфраструктурой. Не пытайтесь «на всех» поднимать
KeepAliveTimeout. - Почему клиенты получают 408? Чаще всего
RequestReadTimeoutдля заголовков/тела срабатывает раньше, чем клиент успевает прислать данные. Проверьте минимальную скорость и стартовые окна, а также прокси/CDN‑буферы перед Apache. - Почему 504 в прокси? Бэкенд не успевает в отведённый
ProxyTimeoutилиProxyPass timeout=. Смотрите, что медленнее — бэкенд или сеть, и корректируйте либо таймауты, либо производительность.
Итог: таймауты в Apache — это не «магические числа», а интерфейс управления ожиданиями: ваших пользователей, сети и бэкендов. Поставьте короткие дефолты, осознанно выделяйте исключения и измеряйте результат.


