Upstream и keepalive: как экономить соединения в Nginx
Nginx может переиспользовать соединения с backend вместо нового TCP-handshake на каждый запрос. Разбираем upstream keepalive, лимиты, proxy_http_version, типичные ошибки и проверку эффекта без выдуманных цифр.

Если Nginx проксирует каждый запрос к приложению через новое TCP-соединение, задержка растёт, а backend тратит ресурсы на handshake и TLS. Связка upstream keepalive и HTTP/1.1 позволяет держать пул неиспользуемых соединений между worker-процессами и переиспользовать их. Ниже — рабочая схема для Nginx OSS 1.25–1.27, критерии выбора размера пула и проверки, что соединения действительно не создаются заново.
Что именно экономит keepalive
У Nginx есть два разных уровня keepalive. Клиентский keepalive оставляет соединение между браузером и Nginx. Upstream keepalive оставляет соединение между Nginx и приложением: например, Gunicorn, uWSGI через HTTP, Node.js, PHP-сервисом или отдельным API.
Эта статья посвящена второму уровню. Он уменьшает количество новых TCP-соединений к backend, но не превращает запросы в очередь и не ограничивает число одновременно обрабатываемых запросов.
Upstream keepalive — это кеш свободных соединений на worker, а не общий пул на весь Nginx.
Keepalive-соединение1 — уже установленное TCP-соединение, которое можно использовать для следующего HTTP-запроса без нового TCP-handshake. При HTTPS внутри upstream дополнительно экономится повторная TLS-рукопожатие.
Минимальная рабочая конфигурация
Пример рассчитан на HTTP-backend, который поддерживает HTTP/1.1 и корректно закрывает соединение по таймауту. В этом случае Nginx не должен отправлять upstream заголовок Connection: close.
http {
upstream application_backend {
server 127.0.0.1:8000;
# До 32 свободных соединений на один worker Nginx
keepalive 32;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://application_backend;
proxy_http_version 1.1;
# Не заставляем upstream закрывать соединение после ответа
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
После изменения сначала проверьте синтаксис командой nginx -t, затем перечитайте конфигурацию штатной командой управления вашим сервисом. Перезапуск без необходимости не нужен: он закрывает уже существующие соединения.
Как работает каждый параметр
keepalive 32задаёт число свободных upstream-соединений, которые один worker может сохранить для этой группы серверов.proxy_http_version 1.1включает HTTP/1.1 при обращении к backend. Для переиспользования соединений это базовый параметр.proxy_set_header Connection ""убирает hop-by-hop-заголовок, который мог бы попросить backend закрыть соединение.proxy_set_header Hostи заголовки с адресом клиента не включают keepalive, но нужны приложению для маршрутизации, логов и формирования абсолютных ссылок.
Как выбрать размер пула
Значение 32 — не универсальная рекомендация, а стартовая точка. Пул создаётся отдельно для каждого worker и upstream-группы. Если у вас 8 worker и keepalive 32, теоретический максимум свободных соединений для одной группы — до 256. Это не означает, что Nginx сразу откроет все 256: соединения появляются по мере трафика и остаются только пока нужны.
Слишком маленький пул снижает эффект: при всплеске запросов Nginx снова создаёт новые соединения. Слишком большой пул удерживает файловые дескрипторы и сокеты на backend. Это особенно заметно, когда upstream-групп несколько.
| Ситуация | Что выбрать | Цена решения |
|---|---|---|
| Один backend, умеренный трафик | Начать с небольшого значения, например 16–32 | При пике часть соединений всё ещё будет создаваться заново |
| Несколько worker и стабильный поток запросов | Считать пул на worker, затем проверить число сокетов | Растёт расход дескрипторов и памяти на Nginx и приложении |
| Много upstream-групп | Задавать keepalive отдельно и учитывать каждую группу | Большое суммарное число простаивающих соединений |
| Редкие запросы с длинными паузами | Небольшой пул либо без него после измерений | Переиспользование будет редким, а соединения могут устаревать |
Два режима и их последствия
Обычный proxy_pass без upstream keepalive
Nginx всё равно проксирует запросы, но свободные соединения к приложению не сохраняются для следующего запроса. На коротких запросах это добавляет лишние операции на каждый новый обмен. Такой режим проще и предсказуемее, но выгоден только там, где запросов мало или backend сам управляет постоянными соединениями иначе.
Upstream keepalive
Nginx после ответа возвращает свободное соединение в кеш worker. Следующий запрос к тому же upstream может получить его без нового подключения. Плата — дополнительные открытые сокеты, необходимость согласовать таймауты и риск накопить слишком большой запас простаивающих соединений.
Сравнивать нужно не только среднее время ответа. Смотрите число новых соединений, долю ошибок, занятость файловых дескрипторов и состояние connection pool приложения.
Что чаще всего ломает схему
Backend закрывает соединение сам
Приложение может использовать короткий keepalive timeout или отправлять Connection: close. Тогда Nginx не сможет долго переиспользовать сокет: он обнаружит закрытие и создаст новый. Таймауты на Nginx и backend нужно сопоставить, но не копировать вслепую.
Неверная версия HTTP
Если оставить проксирование через HTTP/1.0, ожидаемого переиспользования может не быть. Проверьте фактическую конфигурацию, особенно если параметры proxy_http_version задаются в другом location или подключаемом файле.
Слишком большой keepalive
При большом количестве worker итоговое число свободных соединений быстро становится двузначным или трёхзначным. Backend может упереться в лимит соединений раньше, чем вы увидите пользу от пула. Учитывайте также соединения для health-check, административных запросов и других сервисов.
Балансировка меняет ожидание
В upstream-группе с несколькими серверами кеш привязан к конкретному upstream-серверу. Соединение, открытое к одному backend, не перенаправляется на другой. После изменения веса, удаления сервера или перезапуска часть пула будет закрыта и создана заново.
Как измерить эффект, а не угадать
Снимите базовую линию до изменения и повторите тест после него на том же сценарии. Зафиксируйте версию Nginx, число worker, число backend-инстансов, длительность теста, параллельность и тип запроса. Без этих условий цифры нельзя честно сравнивать.
- Сначала проверьте конфигурацию:
nginx -t. Убедитесь, что нужныйlocationдействительно получаетproxy_http_version 1.1. - Снимите метрики backend: количество установленных TCP-соединений, новые соединения в секунду, ошибки и время ответа.
- Повторите одинаковую нагрузку с keepalive и без него. Не меняйте одновременно число worker, лимиты ОС и настройки приложения.
- Сравните не только latency, но и новые TCP-соединения, CPU backend, расход файловых дескрипторов и ошибки 502/504.
Проверять сокеты можно средствами вашей ОС, но конкретная команда зависит от Linux-дистрибутива и доступных утилит. Важно отличать established-соединения от listening-сокетов и смотреть адрес именно backend, а не клиентский трафик к Nginx.
Где подход ломается
Upstream keepalive не исправит медленный SQL-запрос, блокировки в приложении, исчерпание пула соединений к базе или недостаток CPU. Он также не заменяет лимитирование нагрузки: если backend способен обработать 100 запросов одновременно, keepalive не увеличит это число.
Отдельно проверяйте протокол. Для WebSocket, gRPC и долгих потоковых ответов нужны другие настройки прокси; приведённая схема предназначена для обычных HTTP-запросов с ответом, который завершается за разумное время. Инвалидация кешей и настройка client keepalive — отдельные темы.
Что настроить дальше
Начните с небольшой величины keepalive, измерьте новые соединения и расход дескрипторов, затем меняйте только один параметр за раз. В проде поставьте алерты на 502/504, рост established-соединений, ошибки исчерпания файловых дескрипторов и лимит соединений backend. После этого сопоставьте таймауты Nginx и приложения: именно там обычно становится понятно, экономит ли пул соединения или только удерживает лишние сокеты.
1 В этой статье под keepalive имеется в виду переиспользование уже установленного соединения между Nginx и upstream, а не только одноимённый заголовок HTTP.
Проверка рекламы на риски: Что в макете вызовет вопросы ФАС: превосходные степени, сравнения, обещания результата, маркировка.
Спросить за 40 ₽Источники: Nginx documentation: HTTP upstream module, Nginx documentation: HTTP proxy module
Частые вопросы
Нужно ли включать keepalive одновременно для клиента и upstream?
Почему после добавления keepalive число соединений не уменьшилось?
Можно ли поставить keepalive 1000?
Что произойдёт, если backend перезапустится?
Работает ли эта настройка для WebSocket и gRPC?
Может ли keepalive ускорить приложение само по себе?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.