· 17 мин чтения

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

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

Привет! Я Евгений Семенов, директор ITFresh. За мои 15 с лишним лет работы с Nginx-балансировщиками я повидал всякое. И крошечные интернет-магазины с парой десятков заказов в день, и гигантские корпоративные API, где запросы летят со скоростью 8 000 в секунду! Что я вынес из всего этого опыта? Одно правило неизменно: сначала думаешь о надёжности, потом уже о производительности. Точка. В этой статье мы раскроем наш проверенный production-конфиг. Его мы уже два года успешно внедряем у клиентов. Внутри всё как положено: SSL, балансировка, автоматические проверки health checks, и, само собой, развёртывание без простоев – zero-downtime.

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

Давайте честно: если бэкенд у вас всего один, о каком балансировщике речь? Он вам просто не нужен. Вполне хватит обычного reverse proxy. Но вот когда у вас не один сервер, балансировка — это уже мастхэв. А именно, в трёх случаях:

Знаете, что мы видим чаще всего? Клиенты приходят к нам с запросом, который идеально вписывается во второй и третий сценарий одновременно. Они говорят: «Парни, для нас критично, чтобы сервис работал 24/7. Никаких простоев, когда мы там что-то обновляем или перезагружаем!» Наш ответ в таких случаях всегда один и тот же: это значит, что без двух бэкендов и надёжного балансировщика вам не обойтись.

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

Итак, какой минимум нужен для работающей схемы с балансировкой? Это три сервера: один Nginx-фронт и к нему, само собой, две бэкенд-ноды. Если же задачи посерьёзнее, мы обычно рекомендуем ставить два Nginx, обязательно с keepalived для виртуального IP (VIP), и уже три-четыре бэкенда. В нашей практике мы чаще всего используем три вот такие типовые топологии:

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

В большинстве случаев, а это порядка 90% наших заказчиков, схемы Basic вполне достаточно. Но если речь идёт о SLA 99,9% и выше, то тут уже берёмся за HA (High Availability). А вот 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 переключился на офисный Wi-Fi – его сессия, увы, просто потеряется.

Наш совет клиентам всегда один, и мы настаиваем на нём без исключений: если есть хоть малейший шанс, обязательно выносите сессии в Redis. Или, на худой конец, в базу данных. В этом случае любая схема балансировки будет просто летать, а про sticky-сессии можно будет забыть навсегда, за ненадобностью!

Health checks и passive monitoring

Увы, в Nginx OSS (то есть, в бесплатной версии) вы не найдёте встроенного active health check. Там работает только passive. Как это происходит? Nginx отслеживает количество ошибок (fails) для каждого бэкенда, ориентируясь на параметр max_fails в течение определённого fail_timeout. Если сервер превысил этот лимит, Nginx моментально помечает его как недоступный (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 подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.