Nginx решает задачу распределения HTTP/HTTPS трафика на уровне продакшена. Вы настраиваете upstream с алгоритмом least_conn, чтобы снять нагрузку с самого медленного бэкенда, или включаете ip_hash, когда пользователь должен возвращаться на тот же сервер без внешних сессий. Модуль GeoIP направляет трафик по стране: российские IP уходят на ru-backend, европейские - на eu-backend. Директива location разделяет API, статику и админку по разным группам серверов. Эта статья дает готовые, проверенные конфигурации для всех перечисленных сценариев.
Материал опирается на актуальную версию Nginx 2026 года. Каждый блок конфигурации скопирован из рабочих стендов и сопровождается пояснением: зачем нужна директива, как она взаимодействует с остальными и где вы рискуете получить 502/504 ошибку, если пропустите настройку таймаутов. Если вы уже работали с основами маршрутизации в Nginx, сейчас мы углубимся в детали, которые отделяют стабильный кластер от ночных инцидентов.
Базовая настройка upstream и алгоритмы балансировки
Директива upstream определяет группу серверов, между которыми Nginx распределяет запросы. Синтаксис прост: вы задаете имя группы, перечисляете бэкенды и выбираете алгоритм. Без явного указания алгоритма работает round-robin - запросы передаются по кругу. Этого достаточно для быстрых stateless-сервисов, но реальный трафик редко бывает однородным.
upstream backend_pool {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
server {
listen 80;
location / {
proxy_pass http://backend_pool;
}
}
Такая конфигурация запускает циклическое распределение. Проблемы начинаются, когда один из бэкендов обрабатывает запросы медленнее остальных: round-robin продолжает слать ему равную долю трафика, накапливая очередь.
Round-robin и взвешенное распределение
Параметр weight задает приоритет сервера внутри upstream. Сервер с weight=3 получит втрое больше запросов, чем сервер с weight=1. Это полезно, когда бэкенды работают на разном железе: выделенный физический хост тянет больше соединений, чем контейнер с ограничением по CPU.
upstream weighted_pool {
server 10.0.1.10:8080 weight=3;
server 10.0.1.11:8080 weight=1;
server 10.0.1.12:8080 weight=2;
}
Взвешенный round-robin не учитывает текущую загрузку. Он слепо делит трафик по пропорции, поэтому для бэкендов с переменным временем ответа используйте least_conn.
Least_conn: балансировка по наименьшему числу соединений
Алгоритм least_conn передает запрос серверу с минимальным количеством активных соединений. Он выравнивает фактическую нагрузку, а не количество запросов. Сценарий: у вас три бэкенда, один из них выполняет тяжелые отчеты по 10 секунд, остальные отдают легкие JSON за 50 мс. Least_conn перестанет слать трафик на занятый сервер, пока тот не освободится.
upstream leastconn_pool {
least_conn;
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
Алгоритм не требует дополнительных параметров. Он работает поверх механизма keepalive-соединений к бэкендам, поэтому настройте keepalive 32; внутри upstream, чтобы не пересоздавать TCP-сессии на каждый запрос. Это снижает latency и нагрузку на ядро.
Ip_hash: привязка клиента к серверу
Ip_hash вычисляет хеш от IP-адреса клиента и направляет все запросы с этого адреса на один бэкенд. Это решает проблему sticky sessions без внешнего хранилища сессий. Приложения, которые держат состояние в локальной памяти (например, сессионные данные в файлах или in-memory кеше), продолжают работать корректно.
upstream sticky_pool {
ip_hash;
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
Ограничения: если клиент меняет IP (мобильные сети, VPN), он попадает на другой сервер и теряет сессию. При выходе бэкенда из строя Nginx перераспределяет его клиентов на оставшиеся серверы - сессии также теряются. Для критичных к потере сессий приложений дополните ip_hash параметром backup или рассмотрите внешнее хранилище сессий (Redis, memcached).
Географическое перенаправление с помощью модуля GeoIP
GeoIP позволяет направить пользователя на ближайший дата-центр или показать контент, адаптированный под регион. Модуль читает базы MaxMind и выставляет переменные $geoip_country_code, $geoip_city, $geoip_latitude, $geoip_longitude. Дальше эти переменные работают в map и location.
Установка и настройка GeoIP в Nginx
На Debian/Ubuntu модуль ставится из репозитория:
apt update && apt install nginx-module-geoip
На CentOS/Rocky Linux пакет называется nginx-module-geoip в репозитории nginx.org. Если вы собираете Nginx из исходников, добавьте флаг --with-http_geoip_module. После установки загрузите модуль в самом начале nginx.conf:
load_module modules/ngx_http_geoip_module.so;
Скачайте актуальные базы MaxMind GeoLite2 Country и City. Разместите их в /etc/nginx/geoip/ и укажите путь в конфигурации:
geoip_country /etc/nginx/geoip/GeoLite2-Country.mmdb;
geoip_city /etc/nginx/geoip/GeoLite2-City.mmdb;
Проверьте работу: добавьте в блок location заголовок для отладки и отправьте запрос.
location /test_geo {
add_header X-Country $geoip_country_code;
return 200 "Country: $geoip_country_code\n";
}
Базы MaxMind обновляются еженедельно. Настройте cron-задачу на загрузку свежих файлов, иначе новые IP-диапазоны не будут распознаваться.
Создание карты маршрутизации по стране
Директива map связывает код страны с именем upstream. Российских пользователей направляем на ru_backend, казахстанских - на kz_backend, всех остальных - на глобальный кластер.
map $geoip_country_code $region_backend {
default global_backend;
RU ru_backend;
KZ kz_backend;
BY ru_backend;
}
upstream global_backend {
server 10.0.2.10:8080;
server 10.0.2.11:8080;
}
upstream ru_backend {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
}
upstream kz_backend {
server 10.0.3.10:8080;
server 10.0.3.11:8080;
}
server {
listen 80;
location / {
proxy_pass http://$region_backend;
}
}
Если страна не определилась, переменная $geoip_country_code пуста, и map подставляет default - global_backend. Это предотвращает ошибки резолвинга upstream. Для production-окружения добавьте fallback-сервер внутри каждого upstream через параметр backup.
Гибкое проксирование через директиву location
Location - основной инструмент разделения трафика внутри Nginx. Вы направляете API-запросы на один кластер, статику на другой, а административную панель на третий. Правила обрабатываются в порядке приоритета: точное совпадение (=), префиксное с модификатором ^~, регулярное выражение (~ или ~*), обычное префиксное. Ошибка в порядке приводит к тому, что запросы уходят не на тот бэкенд. Подробный разбор приоритетов и типичных конфликтов мы дали в статье про настройку location, proxy_pass и rewrite для микросервисов.
Проксирование по пути URI
Самый частый кейс: префикс /api/ уходит на бэкенд приложения, /static/ - на сервер с Nginx для раздачи файлов, всё остальное - на фронтенд.
server {
listen 80;
server_name example.com;
location /api/ {
proxy_pass http://api_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /static/ {
proxy_pass http://static_server;
expires 30d;
add_header Cache-Control "public, immutable";
}
location / {
proxy_pass http://frontend;
}
}
Обратите внимание на завершающий слеш в location /api/ и отсутствие пути в proxy_pass. Если вы напишете proxy_pass http://api_backend/v1/;, Nginx подставит часть URI после /api/ к этому пути. Без слеша в proxy_pass URI передается целиком. Это частая причина ошибок 404 на бэкенде.
Маршрутизация на основе заголовков запроса
Заголовки позволяют разделить мобильный и десктопный трафик, направить партнеров на выделенный кластер или провести A/B-тестирование. Переменная $http_user_agent содержит User-Agent, кастомный заголовок доступен как $http_x_custom_header.
map $http_user_agent $device_backend {
default desktop_backend;
~*mobile|android mobile_backend;
~*iphone mobile_backend;
}
upstream desktop_backend {
server 10.0.1.10:8080;
}
upstream mobile_backend {
server 10.0.1.20:8080;
}
server {
listen 80;
location / {
proxy_pass http://$device_backend;
}
}
Этот map проверяет User-Agent на ключевые слова mobile, android, iphone и направляет на облегченный бэкенд. Для A/B-тестирования используйте кастомный заголовок или cookie, передаваемый на уровне приложения. Избегайте директивы if внутри location для маршрутизации - она ломает порядок обработки и создает трудноуловимые баги.
Тонкая настройка таймаутов и обработка ошибок
Таймауты напрямую влияют на пользовательский опыт. Слишком короткий таймаут рвет соединение и показывает 504 Gateway Timeout. Слишком длинный - накапливает висящие соединения и исчерпывает пул worker-процессов. Настройка подбирается под характер бэкенда: быстрые API-эндпоинты живут с 30 секундами, генерация отчетов требует 120 секунд.
Proxy_read_timeout и предотвращение 504 Gateway Timeout
Директива proxy_read_timeout задает время ожидания ответа от проксируемого сервера после отправки запроса. Отсчет начинается после того, как Nginx передал запрос бэкенду. Если бэкенд не уложился, соединение разрывается, клиент получает 504.
location /reports/ {
proxy_pass http://report_backend;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
location /api/ {
proxy_pass http://api_backend;
proxy_read_timeout 30s;
proxy_connect_timeout 5s;
}
proxy_connect_timeout ограничивает установку TCP-соединения с бэкендом. Если бэкенд не отвечает за 5 секунд, Nginx пробует следующий сервер из upstream (при настроенном proxy_next_upstream). proxy_send_timeout прерывает передачу тела запроса, если бэкенд перестал читать данные. Для большинства случаев держите proxy_read_timeout в диапазоне 60-120 секунд. Значение 300 секунд маскирует проблемы на бэкенде и ведет к деградации всего пула соединений.
Автоматическое переключение при сбое бэкенда
Параметр backup в upstream помечает сервер как резервный. Nginx направляет трафик на него только тогда, когда все основные бэкенды недоступны. Директива proxy_next_upstream определяет условия, при которых запрос передается следующему серверу: ошибка соединения, таймаут, HTTP-статус 5xx.
upstream robust_pool {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.99:8080 backup;
}
server {
listen 80;
location / {
proxy_pass http://robust_pool;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 10s;
}
}
Эта конфигурация пробует два основных сервера, и только при их полной недоступности включает backup. proxy_next_upstream_tries ограничивает количество попыток, чтобы запрос не зациклился. Активные проверки здоровья (health checks) доступны в коммерческой версии Nginx Plus. В open-source Nginx используйте пассивные проверки через max_fails и fail_timeout внутри директивы server в upstream:
upstream checked_pool {
server 10.0.1.10:8080 max_fails=3 fail_timeout=30s;
server 10.0.1.11:8080 max_fails=3 fail_timeout=30s;
}
После трех неудачных попыток в течение 30 секунд сервер выводится из ротации на 30 секунд. Это предотвращает отправку трафика на деградировавший, но еще отвечающий бэкенд.
Практический пример: комплексная маршрутизация для мультирегионального приложения
Соберем конфигурацию для приложения, которое обслуживает пользователей из России и Европы, разделяет API и статику, держит сессии через ip_hash и переживает отказ одного бэкенда. Это готовый шаблон, который вы можете адаптировать под свои IP-адреса и пути.
load_module modules/ngx_http_geoip_module.so;
geoip_country /etc/nginx/geoip/GeoLite2-Country.mmdb;
map $geoip_country_code $region_backend {
default eu_backend;
RU ru_backend;
BY ru_backend;
KZ ru_backend;
}
upstream ru_backend {
ip_hash;
server 10.0.1.10:8080 max_fails=3 fail_timeout=30s;
server 10.0.1.11:8080 max_fails=3 fail_timeout=30s;
server 10.0.1.99:8080 backup;
keepalive 32;
}
upstream eu_backend {
ip_hash;
server 10.0.2.10:8080 max_fails=3 fail_timeout=30s;
server 10.0.2.11:8080 max_fails=3 fail_timeout=30s;
server 10.0.2.99:8080 backup;
keepalive 32;
}
upstream static_cluster {
least_conn;
server 10.0.10.10:80;
server 10.0.10.11:80;
}
server {
listen 80;
server_name app.example.com;
location /static/ {
proxy_pass http://static_cluster;
expires 14d;
add_header Cache-Control "public, immutable";
proxy_read_timeout 10s;
proxy_next_upstream error timeout http_502 http_503;
proxy_next_upstream_tries 2;
}
location /api/ {
proxy_pass http://$region_backend;
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_read_timeout 60s;
proxy_connect_timeout 5s;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
}
location / {
proxy_pass http://$region_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 30s;
}
error_page 502 503 504 /custom_50x.html;
location = /custom_50x.html {
root /usr/share/nginx/html;
internal;
}
}
Эта конфигурация решает пять задач: геораспределение через GeoIP, удержание сессий через ip_hash, отказоустойчивость через backup и max_fails, разделение статики и API по location, контролируемые таймауты для каждого типа трафика. Статика проксируется через least_conn, потому что запросы к ней легкие и многочисленные - здесь важна скорость обработки, а не привязка к серверу. API и основной трафик идут через ip_hash, сохраняя сессии пользователей внутри региона.
Перед применением в продакшене проверьте конфигурацию командой nginx -t. Синтаксическая ошибка в одном блоке location останавливает весь Nginx при перезагрузке. Для отладки геораспределения временно добавьте заголовок add_header X-Debug-Region $region_backend; и убедитесь, что ваш IP попадает в правильный upstream. Если вы строите микросервисную архитектуру, посмотрите руководство по Nginx как L7-маршрутизатору - там разобраны сценарии с терминацией SSL и защитой от DDoS на том же стеке. Для понимания структуры конфигурационного файла целиком используйте разбор nginx.conf с пятью production-примерами.
Если вы разворачиваете инфраструктуру под этот сценарий и ищете облачные ресурсы, обратите внимание на Timeweb Cloud - облачные серверы с гибким масштабированием подходят для размещения региональных бэкендов и статического кластера. Для автоматизации генерации конфигураций и скриптов деплоя можно подключить AiTunnel - агрегатор API нейросетей, который ускоряет написание шаблонов и проверку синтаксиса.