ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Keepalive к backend: настраиваем Nginx upstream для экономии CPU и сокетов

Постоянные соединения между Nginx и приложением экономят сотни тысяч connect/accept в минуту, разгружают CPU и уменьшают расход эфемерных портов. Разбираем, как включить upstream keepalive для HTTP, FastCGI и TCP, настроить параметры и избежать ловушек.
Keepalive к backend: настраиваем Nginx upstream для экономии CPU и сокетов

Если фронтенд‑прокси на 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.

Иллюстрация пула keepalive в Nginx upstream и переиспользования соединений

Тонкая настройка: сколько и как долго держать соединения

Правильные значения зависят от профиля трафика и числа 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).

Проверка соединений через ss: ESTABLISHED и TIME_WAIT до и после включения keepalive

Тюнинг ядра и лимиты

Keepalive уменьшает поток новых соединений, но лимиты ОС всё равно важны:

  • Дескрипторы: ulimit -n у процессов Nginx и приложения должно покрывать суммарные соединения.
  • Эфемерные порты: при высоком RPS без keepalive можете упираться в диапазон net.ipv4.ip_local_port_range. С keepalive нагрузка на диапазон снижается, но следите за метриками.
  • TIME_WAIT: уменьшится естественным образом. Не злоупотребляйте спорными твиками типа агрессивного переиспользования TIME_WAIT — лучше добейтесь высокого процента переиспользуемых коннектов.
FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Балансировка и sticky‑политики

Пулы соединений формируются на уровне конкретного backend‑сервера внутри upstream. Это хорошо сочетается с алгоритмами least_conn и ip_hash. При использовании sticky‑сессий убедитесь, что размер pool соответствует ожидаемому числу одновременных сессий на инстанс.

Чек‑лист внедрения

  1. Выделите все backend‑цели в именованные upstream.
  2. Для HTTP: включите proxy_http_version 1.1 и proxy_set_header Connection "".
  3. В upstream задайте keepalive, keepalive_requests, keepalive_timeout.
  4. Для FastCGI/uwsgi/scgi: включите fastcgi_keep_conn/uwsgi_keep_conn/scgi_keep_conn.
  5. Для stream‑проксирования: используйте keepalive в блоке stream.
  6. Проверьте метрики: conn/sec к backend, TIME_WAIT, CPU, latency.
  7. Отрегулируйте размеры пула и таймауты по факту, учитывая пиковую нагрузку и авто‑масштабирование.

Расширенные приёмы

  • Разделение локаций по профилю. Для длинных запросов (загрузки, стримы) держите отдельные 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 и сокеты вздыхают свободнее.

Поделиться статьей

Вам будет интересно

Nginx proxy_cache for static: S3 origin, revalidation, cache lock OpenAI Статья написана AI (GPT 5)

Nginx proxy_cache for static: S3 origin, revalidation, cache lock

Пошагово настраиваем Nginx как edge‑кэш для статики с origin в S3: proxy_cache_path и cache key, revalidation через ETag/Last-Modi ...
WP-CLI: смена URL в WordPress после миграции, правка двойных слешей, проверка cron и diagnose OpenAI Статья написана AI (GPT 5)

WP-CLI: смена URL в WordPress после миграции, правка двойных слешей, проверка cron и diagnose

После переноса WordPress чаще ломаются не PHP и не веб-сервер, а данные: в базе остаются старые URL, абсолютные ссылки и «кривые» ...
nftables IPv6 firewall: правильный ICMPv6 и Neighbor Discovery без потери сети OpenAI Статья написана AI (GPT 5)

nftables IPv6 firewall: правильный ICMPv6 и Neighbor Discovery без потери сети

IPv6 чаще «падает» не из‑за адресов, а из‑за слишком строгого firewall: блокируют ICMPv6 и ломают Neighbor Discovery, SLAAC и PMTU ...