· 17 мин чтения

Nginx reverse proxy с SSL и балансировкой нагрузки

Меня зовут Семёнов Евгений Сергеевич, директор АйТи Фреш. За 15+ лет я настраивал балансировщики на Nginx под самые разные нагрузки — от небольшого онлайн-магазина на 20 заказов в день до корпоративных API, через которые проходит 8 000 запросов в секунду. И главное правило не поменялось: сначала думаем про надёжность, потом про производительность. В этой статье — рабочий production-конфиг с SSL, балансировкой, health checks и zero-downtime развёртыванием, который я ставлю клиентам последние два года.

Когда нужна именно балансировка, а не обычный прокси

Если у вас один бэкенд — вам не нужен load balancing. Вам нужен просто reverse proxy. Балансировка нужна в трёх случаях:

У нас на практике самый частый сценарий — второй плюс третий. Клиент говорит: «нам нужно, чтобы не было простоев при перезагрузке сервера». Ответ — два бэкенда и балансировщик.

Архитектура и варианты развёртывания

Минимальная схема с балансировкой — это три сервера: один Nginx-фронт и две бэкенд-ноды. Более серьёзная — два Nginx с keepalived (VIP), три-четыре бэкенда. Я использую три типовых топологии:

ТопологияФронтБэкендКогда
Basic1× Nginx2× appОфис 50+ РМ, внутренний портал
HA2× Nginx + keepalived2–4× appПубличный сайт, продакшен
Multi-DC2× Nginx в ДЦ1 + 2× в ДЦ2, DNS round robin4+ appКрупные B2B, банки

Для 90% заказчиков хватает Basic. HA беру, когда SLA 99,9% и выше. Multi-DC — редкие проекты, обычно это уже не Nginx, а Cloudflare перед инфраструктурой.

Установка и базовая подготовка

Я всегда ставлю Nginx stable из официального репозитория nginx.org. Ядра Linux 5.15+ дают полноценный epoll с SO_REUSEPORT, что критично для высокой нагрузки:

apt install -y nginx
# или из официального репозитория с актуальной версией
# см. https://nginx.org/en/linux_packages.html

Тюн ядра Linux /etc/sysctl.d/99-nginx.conf:

net.core.somaxconn = 65535
net.core.netdev_max_backlog = 16384
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.ip_local_port_range = 1024 65535
fs.file-max = 1000000
sysctl --system
# В /etc/security/limits.conf
nginx soft nofile 100000
nginx hard nofile 100000

Конфиг upstream с балансировкой

Сам upstream-блок — сердце балансировщика. Разберу параметры:

upstream app_cluster {
  # Стратегия: least_conn более справедлив при разной длительности запросов
  least_conn;

  # Бэкенды с параметрами
  server 10.10.10.21:8080 weight=2 max_fails=3 fail_timeout=20s max_conns=256;
  server 10.10.10.22:8080 weight=2 max_fails=3 fail_timeout=20s max_conns=256;
  server 10.10.10.23:8080 weight=1 max_fails=3 fail_timeout=20s max_conns=256 backup;

  # Keepalive к бэкендам — критично для производительности
  keepalive 64;
  keepalive_requests 1000;
  keepalive_timeout 60s;
}

Ключевые параметры сервера:

Полный рабочий server-блок

server {
  listen 80;
  server_name app.example.ru;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl http2;
  server_name app.example.ru;

  # TLS 1.2/1.3, сильные шифры, OCSP stapling
  ssl_certificate     /etc/letsencrypt/live/app.example.ru/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/app.example.ru/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/app.example.ru/chain.pem;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers on;
  ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
  ssl_session_cache shared:SSL:50m;
  ssl_session_timeout 1d;
  ssl_session_tickets off;

  ssl_stapling on;
  ssl_stapling_verify on;

  # HSTS, безопасность
  add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
  add_header X-Frame-Options SAMEORIGIN always;
  add_header X-Content-Type-Options nosniff always;
  add_header Referrer-Policy strict-origin-when-cross-origin always;

  # Тяжёлые запросы
  client_max_body_size 500M;
  client_body_timeout 120s;

  # Балансировка
  location / {
    proxy_pass http://app_cluster;
    proxy_http_version 1.1;
    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;
    proxy_set_header X-Forwarded-Host $host;

    proxy_connect_timeout 5s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;

    proxy_next_upstream error timeout http_502 http_503 http_504;
    proxy_next_upstream_tries 3;
    proxy_next_upstream_timeout 10s;
  }
}

Sticky sessions, когда без них не обойтись

Если приложение хранит сессию в локальной памяти и между бэкендами её нет — используйте ip_hash или sticky cookie (последний только в Nginx Plus). Бесплатный вариант — ip_hash:

upstream app_cluster {
  ip_hash;
  server 10.10.10.21:8080;
  server 10.10.10.22:8080;
  server 10.10.10.23:8080;
}

Проблема ip_hash — за NAT провайдера может прилетать десяток пользователей с одного IP, и они все попадают на одну ноду. Плюс если пользователь меняет сеть (LTE → офис), его сессия теряется.

Я всегда говорю клиентам — если есть возможность, переведите сессии в Redis или БД. Тогда любая балансировка работает корректно, а sticky вам не нужен.

Health checks и passive monitoring

В Nginx OSS встроенного active health check нет — только passive. Схема: Nginx считает fails по max_fails в пределах fail_timeout. Превысили — сервер помечен unavailable.

# В upstream
server 10.10.10.21:8080 max_fails=2 fail_timeout=10s;
# Если 2 запроса подряд дали 5xx/timeout в течение 10с — сервер удаляется на 10с

Для активных проверок я ставлю сторонний модуль nginx_upstream_check_module или использую внешний мониторинг (Prometheus blackbox_exporter) + алерты. На production клиенту обычно хватает passive + мониторинг.

Zero-downtime deploy

Типовой скрипт rolling deploy — обновляем бэкенды по одному. Пример на bash:

#!/bin/bash
BACKENDS=("10.10.10.21:8080" "10.10.10.22:8080" "10.10.10.23:8080")

for b in "${BACKENDS[@]}"; do
  echo "Draining $b..."
  sed -i "s|server $b .*|server $b down;|" /etc/nginx/conf.d/app.conf
  nginx -s reload
  sleep 30   # ждём завершения текущих запросов
  ssh $b "systemctl restart app"
  sleep 10
  sed -i "s|server $b down;|server $b max_fails=2 fail_timeout=20s;|" /etc/nginx/conf.d/app.conf
  nginx -s reload
  echo "$b back in rotation"
done

nginx -s reload не разрывает текущие соединения — старые воркеры отработают свои запросы, новые примут новые. Идеальный zero-downtime.

Реальный кейс: интернет-магазин на Битрикс

В октябре 2025 ко мне обратился интернет-магазин товаров для бани — 1600 SKU, 500–800 заказов в сутки, высокие пики в пятницу-субботу. Всё крутилось на одном Битрикс-сервере, и каждая акция вызывала 502-ки. Нужен был балансировщик и второй бэкенд.

Подняли вторую ноду, идентичную первой (Dell с Xeon Platinum 8280, 64 ГБ RAM), общий файловый каталог через NFS на GlusterFS. Перед ними — Nginx reverse proxy с SSL, least_conn, sticky нет (сессии в Redis). Добавили кеш статики /upload/ на 7 дней, rate limit 100 r/s на /search/. Выделенная подсеть 40G Mellanox между балансировщиком и бэкендами — это важно для Битрикса с его AJAX-запросами.

После запуска: среднее время ответа упало с 1.4 с до 380 мс, 502-ки исчезли, пятничные пики проходят без падений. Тест Black Friday 2025: 2400 RPS в пике, нули ошибок. Стоимость работ — 135 000 руб., плюс железо второго сервера. По словам клиента, окупилось за первую распродажу.

Частые ошибки балансировки

Построим HA на Nginx с балансировкой

Я лично проектирую и внедряю балансировщики на Nginx для веб-приложений и API. SSL, health checks, rolling deploy, keepalived для HA-пары. От 2 до 10 бэкендов, срок — 2–5 рабочих дней.

Телефон: +7 903 729-62-41
Telegram: @ITfresh_Boss
Семёнов Евгений Сергеевич, директор АйТи Фреш

FAQ

Какую стратегию балансировки выбрать?
По умолчанию round robin. Для API с равной нагрузкой на запрос — least_conn. Для приложений с сессиями в памяти — ip_hash или sticky cookie. Для весов по железу — server ... weight=N.
Что такое sticky session и когда его использовать?
Sticky session привязывает пользователя к одному бэкенду, чтобы сессия не терялась при переключении. Нужен если приложение хранит сессию локально. Если сессия в Redis/БД — sticky не нужен, и можно балансировать как угодно.
Как сделать deploy без даунтайма?
Помечаете один бэкенд как down в upstream, ждёте 30 секунд для дренирования, обновляете его, возвращаете в работу, переходите к следующему. Nginx reload делается через SIGHUP — открытые соединения не рвутся.
Нужен ли в Nginx OSS active health check?
Встроенный active health check — только в Nginx Plus. В бесплатной версии используется passive (max_fails + fail_timeout). Для active ставят сторонний модуль nginx_upstream_check или внешние сервисы.
TLS 1.0 и 1.1 — отключать?
Да, обязательно. Оставляем TLS 1.2 и TLS 1.3. 1.0/1.1 имеют уязвимости и не соответствуют PCI-DSS. У меня в конфиге всегда только TLSv1.2 TLSv1.3.

Подпишитесь на рассылку ITfresh

Раз в неделю — практические гайды для руководителя IT и сисадмина: безопасность, 1С, миграции, резервные копии, лайфхаки из реальных проектов.

Реквизиты оператора персональных данных

ООО «АЙТИ-ФРЕШ», ИНН 7719418495, КПП 771901001. Юридический адрес: 105523, г. Москва, Щёлковское шоссе, д. 92, корп. 7. Контакт: info@itfresh.ru, +7 903 729-62-41. Оператор обрабатывает e-mail подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.