Nginx на седьмом уровне OSI анализирует содержимое HTTP-запросов - заголовки, URI, куки, тип контента - и принимает решение о маршрутизации на основе этих данных. Такой подход даёт системному администратору полный контроль над трафиком: можно направить запросы к API на один пул серверов, статику раздавать с другого, а запросы из определённых регионов блокировать ещё до попадания в приложение. Результат - снижение времени отклика, равномерная загрузка backend и эшелонированная защита периметра.
Эта статья - практическое руководство по настройке Nginx как L7-маршрутизатора. Разберём location-блоки для разделения трафика, map-директивы для условной маршрутизации, терминацию SSL, балансировку нагрузки с проверками здоровья и защиту от базовых DDoS-атак. Каждый раздел содержит готовые конфигурации, которые можно адаптировать под production-среду 2026 года.
Почему маршрутизация на уровне приложений - ключ к скорости и безопасности
Маршрутизаторы четвёртого уровня (L4) оперируют IP-адресами и портами. Они распределяют TCP-соединения, но не заглядывают внутрь трафика. Nginx на уровне L7 разбирает HTTP-протокол и принимает решение по сотням параметров: URL запроса, значению заголовка Accept-Language, наличию определённой куки, HTTP-методу. Эта глубина анализа открывает сценарии, недоступные на L4.
Практическая выгода для администратора в 2026 году укладывается в три направления. Первое - скорость: кэширование ответов API и статических ресурсов на стороне Nginx снижает нагрузку на backend в разы. Второе - отказоустойчивость: балансировка с проверками здоровья исключает отправку трафика на упавший сервер. Третье - безопасность: ограничение частоты запросов и фильтрация по геопризнаку блокируют атаки до того, как они достигнут приложения. Продвинутая безопасность Nginx раскрывает эшелонированную защиту подробнее, а здесь мы сфокусируемся на маршрутизации как первом рубеже.
Location-блоки: фундамент интеллектуальной маршрутизации запросов
Директива location определяет, какой блок конфигурации обработает запрос. Nginx сопоставляет URI запроса с шаблонами, заданными в location, и выбирает наиболее специфичное совпадение. Ошибка в приоритете - частая причина, почему конфигурация не работает как ожидалось.
Типы location и их приоритет: как Nginx выбирает блок
Nginx различает четыре типа location. Порядок их обработки фиксирован и не зависит от очерёдности в конфигурационном файле.
| Модификатор | Тип совпадения | Приоритет |
|---|---|---|
= | Точное совпадение URI | 1 (высший) |
^~ | Префиксное совпадение с приоритетом над regex | 2 |
~ или ~* | Регулярное выражение (с учётом регистра и без) | 3 |
| Без модификатора | Префиксное совпадение | 4 (низший) |
Алгоритм выбора: Nginx сначала ищет точное совпадение (=). Если его нет - ищет префиксное с модификатором ^~. Далее проверяет регулярные выражения в порядке их появления в конфигурации. Если ни одно регулярное выражение не подошло - использует самое длинное префиксное совпадение без модификатора.
Распространённая ошибка - размещение regex-location до префиксного с ^~. Разработчик ожидает, что запрос к /static/img/logo.png обработается префиксным блоком, но регулярное выражение перехватывает его первым. Решение: явно указывать ^~ для критичных префиксных путей.
Практический пример: разделение статики, API и админ-панели
Типичный сценарий для веб-приложения: статические файлы отдаются напрямую с диска, API-запросы проксируются на backend, а админ-панель доступна только из внутренней сети.
server {
listen 80;
server_name example.com;
# Точное совпадение - заглушка для health-check
location = /health {
return 200 "OK";
add_header Content-Type text/plain;
}
# Статика - префиксное с приоритетом
location ^~ /static/ {
root /var/www/example;
expires 30d;
add_header Cache-Control "public, immutable";
}
# API - регулярное выражение
location ~ ^/api/v[12]/ {
proxy_pass http://api_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Админ-панель - ограничение по IP
location ~ ^/admin/ {
allow 10.0.0.0/8;
deny all;
proxy_pass http://admin_backend;
}
# Всё остальное - на фронтенд
location / {
proxy_pass http://frontend;
}
}
Блок = /health срабатывает мгновенно - Nginx не проверяет другие location. Блок ^~ /static/ гарантированно обрабатывает статику, даже если в конфигурации позже появится regex, совпадающий с путём. API-запросы маршрутизируются по версиям через регулярное выражение - это позволяет развести v1 и v2 на разные upstream без правки приложения.
Подробный разбор структуры конфигурационного файла с секциями main, events, http, server и location есть в руководстве по nginx.conf. Для сложных микросервисных сценариев с rewrite-правилами рекомендую настройку маршрутизации в микросервисах.
Map и переменные: гибкая маршрутизация без перезагрузки
Директива map создаёт переменную, значение которой зависит от другой переменной. Она определяется на уровне http и вычисляется в момент обработки запроса. Изменение map-правил требует только перезагрузки конфигурации (nginx -s reload), а не рестарта сервера.
Map для условной маршрутизации: пример с версиями API
Задача: клиенты отправляют заголовок Accept-Version со значением v1 или v2. Нужно направить запрос в соответствующий upstream без правки location-блоков.
map $http_accept_version $api_upstream {
default api_v1;
"v2" api_v2;
"v3" api_v3;
}
upstream api_v1 {
server 10.0.1.1:8080;
server 10.0.1.2:8080;
}
upstream api_v2 {
server 10.0.2.1:8080;
server 10.0.2.2:8080;
}
upstream api_v3 {
server 10.0.3.1:8080;
}
server {
location /api/ {
proxy_pass http://$api_upstream;
}
}
Переменная $http_accept_version - это значение HTTP-заголовка Accept-Version, нормализованное Nginx (префикс http_, дефисы заменены на подчёркивания, нижний регистр). Если заголовок отсутствует или его значение не совпадает ни с одним ключом - map возвращает default. Результат - один location для всех версий API, а логика маршрутизации вынесена в map-блок. Деплой новой версии сводится к добавлению upstream и записи в map.
Переменные Nginx: что нужно знать для тонкой настройки
Встроенные переменные - строительный материал для map, if и пользовательской логики. Ключевые переменные для маршрутизации:
$host- значение заголовка Host из запроса, в нижнем регистре$uri- нормализованный URI без аргументов (декодирован, без /../ и //)$args- строка аргументов после ? в URL$scheme- http или https$remote_addr- IP-адрес клиента$http_user_agent- значение заголовка User-Agent$cookie_имя- значение куки с указанным именем
Пример гео-маршрутизации через map и GeoIP (модуль ngx_http_geoip_module):
map $geoip_country_code $backend_country {
default global_backend;
RU ru_backend;
DE eu_backend;
FR eu_backend;
}
server {
location / {
proxy_pass http://$backend_country;
}
}
Запросы из России направляются на серверы в локальном ЦОДе, из Германии и Франции - в европейский кластер, остальные - на глобальный балансировщик. Задержка снижается на десятки миллисекунд без изменения кода приложения.
Терминация SSL: настройка безопасного соединения за 5 минут
Терминация SSL на Nginx означает, что клиент устанавливает зашифрованное соединение с Nginx, а трафик между Nginx и backend-серверами идёт по HTTP. Это разгружает приложение от криптографических операций и централизует управление сертификатами.
Минимальная конфигурация для HTTPS с сертификатом от Let's Encrypt:
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://backend;
}
}
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
Проверка через SSL Labs покажет рейтинг B - этого достаточно для старта, но не для production. Нужна оптимизация.
Оптимизация SSL: протоколы, шифры и сессии
На 2026 год минимальный стандарт безопасности - TLS 1.2, рекомендуемый - TLS 1.3. Конфигурация, которая даёт рейтинг A+ на SSL Labs:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1h;
ssl_session_tickets off;
# OCSP Stapling - ускоряет проверку сертификата
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 1.1.1.1 valid=300s;
resolver_timeout 5s;
Директива ssl_session_cache с ключом shared выделяет 10 мегабайт общей памяти для кэша сессий - повторные подключения клиента выполняются без полного handshake. ssl_stapling включает OCSP Stapling: Nginx сам проверяет статус сертификата и прикрепляет ответ к handshake, избавляя браузер клиента от отдельного запроса к CA. Время установки соединения сокращается на 20-30%.
Балансировка нагрузки: распределяем трафик для отказоустойчивости
Директива upstream определяет группу backend-серверов. Nginx распределяет запросы между ними по заданному алгоритму. Минимальная конфигурация:
upstream backend {
server 10.0.1.1:8080;
server 10.0.1.2:8080;
server 10.0.1.3:8080;
}
server {
location / {
proxy_pass http://backend;
}
}
По умолчанию используется round-robin - запросы распределяются равномерно по кругу. Для проектов с состоянием (stateful) этот метод не подходит: сессия пользователя может оказаться на разных серверах.
Методы балансировки: когда что использовать
| Метод | Директива | Когда применять |
|---|---|---|
| Round-robin | по умолчанию | Stateless-сервисы, одинаковая мощность backend |
| Least connections | least_conn; | Запросы с разным временем обработки, неоднородные серверы |
| IP hash | ip_hash; | Сессионные приложения без внешнего хранилища сессий |
| Generic hash | hash $variable; | Привязка запроса к серверу по произвольному ключу (например, $request_uri для кэша) |
Пример с least_conn для API с дорогими запросами:
upstream api_backend {
least_conn;
server 10.0.1.1:8080 weight=3;
server 10.0.1.2:8080 weight=1;
server 10.0.1.3:8080 backup;
}
Параметр weight задаёт пропорцию: сервер 10.0.1.1 получает втрое больше запросов, чем 10.0.1.2. Сервер с флагом backup включается в работу только при отказе всех основных.
Активные проверки здоровья: как не слать трафик на упавший сервер
Пассивные проверки (встроенные в open-source Nginx) работают по факту ошибки: если backend вернул ошибку, Nginx помечает его как нерабочий на заданное время. Настройка через proxy_next_upstream и параметры server:
upstream backend {
server 10.0.1.1:8080 max_fails=3 fail_timeout=30s;
server 10.0.1.2:8080 max_fails=3 fail_timeout=30s;
}
server {
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_500 http_502 http_503;
proxy_next_upstream_tries 2;
proxy_connect_timeout 2s;
}
}
При трёх неудачных попытках за 30 секунд сервер исключается из ротации на 30 секунд. proxy_next_upstream определяет, какие ошибки считаются основанием для перехода к следующему серверу. Для коммерческой версии Nginx Plus доступны активные проверки с директивой health_check, которая периодически опрашивает backend и выводит сервер из ротации превентивно.
Защита от DDoS: ограничение соединений и запросов
Модули ngx_http_limit_conn_module и ngx_http_limit_req_module ограничивают количество одновременных соединений и частоту запросов. Это первая линия обороны против HTTP flood и Slowloris.
Настройка limit_req: защита от перебора и флуда
# В блоке http
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# В блоке server или location
location /login {
limit_req zone=login burst=3 nodelay;
proxy_pass http://auth_backend;
}
Директива limit_req_zone создаёт зону памяти login размером 10 мегабайт (хватает на ~160 тысяч уникальных IP) и задаёт базовую скорость - 5 запросов в минуту с одного IP. burst=3 разрешает кратковременный всплеск до трёх запросов сверх лимита, которые ставятся в очередь. nodelay заставляет обрабатывать burst-запросы немедленно, без задержки - подходит для интерактивных страниц.
Мониторинг срабатываний настраивается через отдельный лог-файл:
limit_req_log_level warn;
# В location добавляем:
limit_req_status 429;
Клиенты, превысившие лимит, получают HTTP 429 Too Many Requests. Факт ограничения пишется в error.log с уровнем warn - это позволяет отслеживать аномалии через системы агрегации логов.
Комплексный пример: защита WordPress-сайта
WordPress - частая цель атак: перебор паролей через wp-login.php, эксплуатация xmlrpc.php, сканирование уязвимостей. Конфигурация, закрывающая эти векторы:
http {
# Зона для wp-login - жёсткий лимит
limit_req_zone $binary_remote_addr zone=wp_login:10m rate=2r/m;
# Зона для xmlrpc - блокировка после первого же запроса
limit_req_zone $binary_remote_addr zone=wp_xmlrpc:10m rate=1r/m;
# Гео-блокировка через map
map $geoip_country_code $allow_country {
default yes;
CN no;
RU yes;
}
server {
# Отклоняем запросы из нежелательных стран
if ($allow_country = no) {
return 403;
}
location = /wp-login.php {
limit_req zone=wp_login burst=1 nodelay;
limit_req_status 429;
proxy_pass http://wordpress_backend;
}
location = /xmlrpc.php {
limit_req zone=wp_xmlrpc burst=0;
limit_req_status 403;
return 403;
}
}
}
Два запроса в минуту к wp-login.php с одного IP - легитимный пользователь не заметит ограничения, брутфорс остановится. xmlrpc.php блокируется полностью, если он не используется. Гео-блокировка через map отсекает трафик из стран, где у проекта нет аудитории. Комбинирование map с limit_req даёт эшелонированную защиту без внешних WAF.
Интеллектуальное кэширование: ускоряем отклик и снижаем нагрузку
Кэширование ответов backend на стороне Nginx уменьшает время ответа с сотен миллисекунд до единиц и кратно снижает нагрузку на приложение. Директива proxy_cache_path задаёт хранилище на диске, proxy_cache активирует кэш для конкретного location.
Кэширование динамического контента: настройка proxy_cache
http {
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:100m
max_size=10g inactive=60m use_temp_path=off;
server {
location /api/ {
proxy_cache api_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_bypass $cookie_nocache $arg_nocache;
proxy_no_cache $cookie_nocache $arg_nocache;
proxy_pass http://api_backend;
add_header X-Cache-Status $upstream_cache_status;
}
}
}
Параметры proxy_cache_path: levels=1:2 задаёт двухуровневую структуру каталогов для быстрого поиска файла, keys_zone выделяет 100 мегабайт общей памяти под ключи, max_size ограничивает общий объём кэша на диске, inactive удаляет записи, к которым не обращались 60 минут.
proxy_cache_key формирует уникальный ключ из схемы, метода, хоста и URI - это гарантирует, что POST-запросы не получат кэшированный ответ от GET. proxy_cache_bypass и proxy_no_cache позволяют обойти кэш по куке или параметру запроса - полезно для отладки и авторизованных пользователей. Заголовок X-Cache-Status показывает, был ли ответ взят из кэша (HIT), сохранён (MISS) или кэш обойдён (BYPASS) - незаменим при отладке.
Для проектов с загрузкой больших файлов важно правильно настроить буферизацию. Оптимизация загрузки больших файлов в Nginx содержит готовые конфигурации для устранения ошибки 413 и стабильной работы с объёмными данными.
Отладка и тестирование: как не сломать продакшен
Каждое изменение конфигурации должно проходить проверку синтаксиса до применения. Команда nginx -t парсит все подключённые файлы и сообщает об ошибках с указанием строки. Запускайте её перед каждым reload:
nginx -t && nginx -s reload
Логи - основной инструмент диагностики. access.log фиксирует каждый обработанный запрос, error.log содержит предупреждения и ошибки. Для отладки маршрутизации временно повысьте уровень error_log до debug и добавьте пользовательский заголовок в location:
location /api/ {
add_header X-Debug-Location "api_block" always;
proxy_pass http://api_backend;
}
Заголовок с директивой always добавляется даже при ошибках - вы точно узнаете, какой location обработал запрос. После отладки удалите или закомментируйте эту строку.
Типовые ошибки при настройке маршрутизации:
- Location не матчится. Проверьте приоритеты: regex мог перехватить запрос раньше префиксного блока. Добавьте
^~к префиксному location или пересмотрите порядок регулярных выражений. - SSL не подхватывается. Убедитесь, что пути к сертификатам абсолютные, файлы читаемы пользователем nginx, а в конфигурации нет опечаток в директивах
ssl_certificateиssl_certificate_key. - Проксирование обрезает URI. Если
proxy_passуказан без URI, Nginx передаёт исходный URI. Если с URI (proxy_pass http://backend/app/) - заменяет совпавшую часть location на указанный путь. Несоответствие ломает маршруты приложения.
Перед развёртыванием в production протестируйте конфигурацию на staging-окружении с идентичной структурой каталогов и версией Nginx. Различия в путях или модулях между окружениями - источник трудновоспроизводимых багов.