Если фронтенд‑прокси на Nginx по каждому запросу заново открывает TCP‑соединение к приложению, вы платите лишними системными вызовами, контекст‑свитчами и TIME_WAIT‑ами. Включение keepalive к backend переводит связку «Nginx → приложение» на постоянные соединения и заметно экономит CPU и сокеты. Все примеры ниже предполагают доступ к конфигурации Nginx на своём сервере, например на VDS.
Что такое keepalive к backend и зачем он нужен
«Клиентский» keep‑alive (между браузером и Nginx) вы, скорее всего, уже используете. Но есть и «серверный» — между Nginx и вашими backend‑ами. Смысл тот же: переиспользовать уже установленный TCP‑канал для многих запросов вместо постоянных connect/close.
Экономия проявляется сразу в двух местах:
- меньше системных вызовов connect/accept и TLS‑рукопожатий (если Nginx шифрует до backend; позаботьтесь о валидных SSL-сертификаты),
- меньше выбегает эфемерных портов и меньше TIME_WAIT, что снижает риск порт‑exhaustion под нагрузкой.
Практика показывает: при коротких API‑запросах keepalive к backend снижает загрузку CPU на 10–30%, а задержки p50–p95 — на 5–20% за счёт удаления «тряски» TCP и системных накладных расходов.
Как это работает в Nginx для HTTP upstream
За переиспользование соединений к HTTP‑backend отвечает модуль ngx_http_upstream_keepalive_module. Он создаёт пул «праздных» соединений на воркер и возвращает их в работу при новых запросах.
keepalive N— максимальное число неиспользуемых (idle) соединений на воркер к каждому upstream‑серверу.keepalive_requests N— сколько запросов можно обработать по одному соединению, после чего оно закрывается и создаётся заново (ротация).keepalive_timeout T— сколько держать соединение в idle до закрытия.
Важно: пул соединений в Nginx — на каждый воркер и на каждый upstream‑сервер. Если у вас 4 воркера и keepalive 64, теоретический максимум — 4 × 64 idle‑коннекта на сервер внутри upstream.
Минимальный рабочий пример (HTTP → приложение на :8080)
upstream app_backend {
server 127.0.0.1:8080 max_fails=3 fail_timeout=10s;
keepalive 64;
keepalive_requests 1000;
keepalive_timeout 60s;
}
server {
listen 80;
location /api/ {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 30s;
proxy_connect_timeout 1s;
proxy_pass http://app_backend;
}
}
Ключевые моменты:
proxy_http_version 1.1— нужен для полноценного keep‑alive к backend.proxy_set_header Connection ""— убирает заголовокConnection, чтобы Nginx мог переиспользовать соединение. Если оставитьclose, пул не заработает.keepaliveи сопутствующие параметры задаются внутриupstream, а не вserver.

Тонкая настройка: сколько и как долго держать соединения
Правильные значения зависят от профиля трафика и числа backend‑воркеров.
- Размер пула (
keepalive). Возьмите пиковое число одновременных запросов к конкретному backend с одного Nginx‑воркера и добавьте 10–20% запаса. На практике 32–256 на сервер — хороший диапазон. - Жизненный цикл (
keepalive_requests). Ротация соединений предотвращает накопление долгоживущих проблем (например, «липких» утечек в приложении или «усталость» прокси). Типичные значения — 1000–10000. - Idle‑таймаут (
keepalive_timeout). Если бёрст трафика неравномерен, держите 30–120 секунд. Для сверхдинамичных сред — 15–30 секунд, чтобы не мешать autoscale и не держать лишние дескрипторы.
Помните: соединения принадлежат воркеру Nginx. Если включён
worker_cpu_affinityили много воркеров, распараллеливание повышает общий верхний предел соединений.
FastCGI/uwsgi/scgi: keepalive к PHP‑FPM и не только
Для FastCGI и родственных протоколов действует другое правило: используйте директивы fastcgi_keep_conn, uwsgi_keep_conn, scgi_keep_conn. Они включают постоянные соединения к соответствующим backend‑ам.
upstream php_fpm {
server unix:/run/php/php-fpm.sock;
# Для сокета keepalive не актуален, но для TCP-порта можно держать pool
}
server {
listen 80;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass php_fpm;
fastcgi_keep_conn on;
fastcgi_read_timeout 30s;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Если PHP‑FPM на TCP‑порту, fastcgi_keep_conn on позволит Nginx удерживать соединения к пулу FPM‑воркеров. Это уменьшит нагрузку на accept() в FPM и сократит conn/sec между Nginx и FPM. Если проект размещён на общем хостинге, дополнительно посмотрите про оптимизации PHP и компрессию: OPcache и Brotli на shared‑хостинге.
TCP (stream) проксирование
Для TCP‑балансировки в блоке stream есть свой keepalive, который управляет переиспользованием соединений к upstream‑серверу на уровне транспортного слоя (без понимания HTTP).
stream {
upstream tcp_backend {
server 127.0.0.1:9001 max_fails=3 fail_timeout=10s;
keepalive 100;
}
server {
listen 9000;
proxy_connect_timeout 1s;
proxy_timeout 10m;
proxy_pass tcp_backend;
}
}
Это полезно для протоколов поверх TCP, когда многочисленные короткие сессии порождают лавину connect/close. Следите за тем, чтобы приложение корректно выдерживало долгоживущие соединения.
Не путать: клиентский keep‑alive vs upstream keep‑alive
В Nginx есть похожие по названию параметры для клиентской стороны, например keepalive_timeout и keepalive_requests в контексте http/server. Они управляют соединениями «клиент ↔ Nginx» и не влияют на «Nginx ↔ backend». Для backend применяйте параметры внутри upstream или fastcgi_keep_conn и аналоги.
Типичные ловушки и как их обойти
- Backend сам закрывает соединение. Многие фреймворки ставят низкий серверный keepalive timeout (например, 5–15 секунд). Согласуйте значения — пусть backend держит чуть дольше, чем Nginx.
- Заголовок
Connectionперезаписывается. Любыеproxy_set_header Connection closeвlocationубьют переиспользование. ДержитеConnection "", а при апгрейде вебсокетов выставляйте это только в соответствующемlocation. - Переменная в
proxy_pass. Если вы пишетеproxy_pass http://$upstream;, Nginx не сможет применить upstream‑пул. Определите именованныйupstreamи делайтеproxy_pass http://name;. - Слишком большой пул. Высокие значения
keepaliveудерживают много дескрипторов и мешают авто‑масштабированию приложений (соединения остаются «прилипшими» к старым инстансам). Лечится корректнымkeepalive_timeoutи умеренным размером пула. - Долгие запросы. SSE и вебсокеты занимают коннект надолго; для них выигрыша от keepalive нет. Разводите такие локации отдельно.
- Несоответствие HTTP‑версии. Без
proxy_http_version 1.1к backend может уйти HTTP/1.0, который не поддерживает полноценный keep‑alive.
Наблюдение и проверка
Проверить, что пул работает, можно системными инструментами и логикой Nginx.
- Системные соединения. Смотрите число ESTABLISHED к backend и долю TIME_WAIT до и после включения пула.
# Все TCP-соединения к backend-порту
ss -tan dst :8080
# Краткая сводка по состояниям
ss -s
- Логи запросов. Сравните средние задержки upstream до/после. Добавьте в формат
$upstream_response_timeи$upstream_connect_time.
log_format main '$remote_addr - $request $status $body_bytes_sent up_conn=$upstream_connect_time up_resp=$upstream_response_time';
access_log /var/log/nginx/access.log main;
- Стабильность под нагрузкой. Во время стресс‑теста смотрите CPU у Nginx и приложения, число соединений и кол‑во conn/sec (падение последнего указывает на работу keepalive).

Тюнинг ядра и лимиты
Keepalive уменьшает поток новых соединений, но лимиты ОС всё равно важны:
- Дескрипторы:
ulimit -nу процессов Nginx и приложения должно покрывать суммарные соединения. - Эфемерные порты: при высоком RPS без keepalive можете упираться в диапазон
net.ipv4.ip_local_port_range. С keepalive нагрузка на диапазон снижается, но следите за метриками. - TIME_WAIT: уменьшится естественным образом. Не злоупотребляйте спорными твиками типа агрессивного переиспользования TIME_WAIT — лучше добейтесь высокого процента переиспользуемых коннектов.
Балансировка и sticky‑политики
Пулы соединений формируются на уровне конкретного backend‑сервера внутри upstream. Это хорошо сочетается с алгоритмами least_conn и ip_hash. При использовании sticky‑сессий убедитесь, что размер pool соответствует ожидаемому числу одновременных сессий на инстанс.
Чек‑лист внедрения
- Выделите все backend‑цели в именованные
upstream. - Для HTTP: включите
proxy_http_version 1.1иproxy_set_header Connection "". - В
upstreamзадайтеkeepalive,keepalive_requests,keepalive_timeout. - Для FastCGI/uwsgi/scgi: включите
fastcgi_keep_conn/uwsgi_keep_conn/scgi_keep_conn. - Для stream‑проксирования: используйте
keepaliveв блокеstream. - Проверьте метрики: conn/sec к backend, TIME_WAIT, CPU, latency.
- Отрегулируйте размеры пула и таймауты по факту, учитывая пиковую нагрузку и авто‑масштабирование.
Расширенные приёмы
- Разделение локаций по профилю. Для длинных запросов (загрузки, стримы) держите отдельные
locationс другими таймаутами, чтобы не «забивать» пул. - read timeout vs keepalive timeout.
proxy_read_timeoutдолжен быть не меньше, чем максимальная длительность ответа.keepalive_timeoutотвечает только за idle‑стадию между запросами. - Пулы и autoscale. Если backend часто масштабируется, держите умеренные
keepaliveи не слишком большиеkeepalive_timeout, чтобы коннекты быстро перетекали на новые инстансы.
Краткое резюме
Upstream keepalive в Nginx — один из самых недорогих способов снизить системные накладные расходы и стабилизировать задержки. Для HTTP включите proxy_http_version 1.1, очистите Connection, задайте разумные keepalive/keepalive_requests/keepalive_timeout. Для PHP‑FPM используйте fastcgi_keep_conn on, а для TCP‑прокси — keepalive в блоке stream. Проверьте метрики и отрегулируйте значения под свой трафик — и вы увидите, как падает шум соединений, а CPU и сокеты вздыхают свободнее.


