Шлюз Nginx пропускает допустимый поток запросов и ограничивает всплеск
DevOps

Как ограничить частоту запросов и соединений в Nginx

Настраиваем limit_req и limit_conn в Nginx: выбираем ключ, rate и burst, учитываем CDN и NAT, включаем dry run, проверяем 429 и читаем журнал.

Содержание

Форма входа получает сотни попыток за несколько секунд, а обычный пользователь отправляет один-два запроса. Nginx может ограничить частоту для одного адреса и сгладить короткий всплеск до того, как он займёт все рабочие процессы приложения.

Такой лимит защищает конкретный дорогой маршрут и уменьшает последствия ошибки клиента. Он не заменяет аутентификацию, блокировку учётной записи и внешнюю защиту от DDoS: трафик уже дошёл до вашего сервера.

Сначала выберите операцию и ключ

Не начинайте с глобального ограничения всего сайта. Статика, API, загрузка файла и вход имеют разную нормальную частоту. Запишите обычный пик, допустимый всплеск, стоимость одного запроса и желаемый ответ при превышении.

Чаще всего ключом служит IP-адрес в бинарном виде — $binary_remote_addr. Если перед Nginx стоит CDN или балансировщик, сначала настройте исходный IP и границу доверия. Иначе тысячи посетителей разделят один адрес proxy и попадут под общий лимит.

IP тоже не всегда равен одному человеку: офис, мобильный оператор и домашняя сеть используют NAT. Для авторизованного API приложение может ограничивать по учётной записи, а Nginx — дополнительно сдерживать грубый всплеск по адресу.

Объявите общую зону

В контексте http создайте область памяти для состояний клиентов. Следующая зона называется login_per_ip, использует бинарный IP как ключ и допускает в среднем пять запросов в секунду на один ключ:

limit_req_zone $binary_remote_addr zone=login_per_ip:10m rate=5r/s;

Размер 10 МБ хранит ключи и счётчики, а не тела запросов. Требуемая память зависит от числа одновременно отслеживаемых адресов. Если зона переполнится, новые записи создать не получится; следите за журналом и не назначайте размер случайно маленьким.

Примените предел к одному маршруту

Внутри точного location подключите зону. burst=10 разрешает короткий всплеск до десяти запросов сверх устойчивой скорости, а nodelay не заставляет допустимые запросы ждать в очереди:

location = /api/login {
    limit_req zone=login_per_ip burst=10 nodelay;
    limit_req_status 429;

    proxy_pass http://127.0.0.1:3000;
}

Код 429 означает Too Many Requests и понятнее клиенту, чем стандартный для модуля код отказа. API должно показывать ясное сообщение и не обещать точное время ожидания, если оно не рассчитывается.

burst не превращает скорость 5 r/s в постоянные 15 r/s. Он лишь допускает краткий наплыв. Без nodelay часть запросов задерживается для выравнивания потока; это может занять соединения и ухудшить отклик интерактивного API.

Сначала наблюдайте без блокировки

Если ваша версия Nginx поддерживает dry run, временно включите его рядом с limit_req. Модуль посчитает превышения и запишет их, но не отклонит запросы:

limit_req_dry_run on;
limit_req_log_level notice;

Проверьте поддержку директивы через документацию установленной версии и nginx -t. Соберите нормальный период, включая рабочий пик. Затем оцените, сколько законных пользователей превысили порог и почему.

Dry run нельзя оставлять как единственную «защиту»: он нужен для настройки. Перед включением блокировки зафиксируйте измеренный rate, burst, область действия и способ быстрого отключения.

Ограничьте параллельные соединения отдельно

limit_req считает частоту запросов. Для долгих загрузок или потоков дополнительно может понадобиться число одновременных соединений. Зона соединений также объявляется в http:

limit_conn_zone $binary_remote_addr zone=connections_per_ip:10m;

В нужном server или location подключите предел. Следующая строка разрешает одному ключу до двадцати учитываемых соединений:

limit_conn connections_per_ip 20;
limit_conn_status 429;

Число 20 — пример. При HTTP/2 и HTTP/3 параллельные запросы учитываются модулем отдельно, хотя используют одно транспортное соединение. WebSocket и длительные скачивания тоже меняют картину. Проверяйте реальный протокол и сценарий, а не только число вкладок.

Не ограничивайте служебные проверки случайно

Мониторинг, health-check балансировщика и webhook внешнего сервиса могут обращаться чаще пользователя. Не добавляйте широкое исключение по заголовку, который может подделать клиент. Выделите служебный маршрут, ограничьте сеть и примените отдельную политику.

Не отключайте лимит для всех «известных ботов» по User-Agent. Заголовок свободно меняется и не подтверждает владельца запроса.

Проверьте в контролируемой среде

После nginx -t и reload сначала выполните несколько обычных запросов. Затем в staging отправьте короткую параллельную серию и выведите только HTTP-коды:

seq 1 20 | xargs -P5 -I{} \
  curl -sS -o /dev/null -w '%{http_code}\n' \
  'https://staging.example.com/api/login?probe={}'

Команда запускает до пяти curl одновременно. Не используйте её против чужого сервиса или production без окна и наблюдения. В результате должны быть видны успешные коды и, при превышении выбранной границы, 429.

Сопоставьте тест с error log и access log. Запишите $limit_req_status в диагностический формат: значения PASSED, DELAYED, REJECTED и их dry-run варианты показывают решение модуля.

Разберите ложные срабатывания

Если блокируется офис целиком, причиной может быть NAT. Если блокируются все посетители, проверьте $remote_addr за CDN. Если ограничение легко обходится сменой IPv6-адреса или распределённой сетью, не пытайтесь бесконечно уменьшать rate: нужна защита на другом уровне и прикладная проверка поведения.

При резком росте 429 смотрите одновременно нагрузку приложения и число уникальных ключей. Один ошибочный клиент и распределённая атака требуют разных действий.

Откат и дальнейшее наблюдение

Чтобы быстро вернуть прежнее поведение, удалите только limit_req или включите dry run, затем проверьте конфигурацию и выполните reload:

sudo nginx -t
sudo systemctl reload nginx
sudo tail -n 50 /var/log/nginx/error.log

Объявленная, но не используемая зона сама запросы не блокирует. После отката убедитесь, что законный сценарий снова работает и 429 исчезли. Затем исправьте ключ или значения на основании журналов.

Таймауты и размеры запроса настраиваются отдельно по руководству о лимитах Nginx, а результаты удобнее анализировать через расширенный access log.

Первоисточники

Рекламное местоВаша компания здесьРазместить рекламу

Самопроверка

Проверьте, что материал усвоен

Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.

01Где объявляется limit_req_zone?
02Что делает nodelay вместе с burst?
03Что проверить перед лимитом по IP?

Разбираем коротко

Частые вопросы

Защищает ли limit_req от любой DDoS-атаки?

Нет. Ограничение Nginx действует после достижения сервера и расходует его сеть и ресурсы. Крупные атаки требуют фильтрации у провайдера или внешнего защитного сервиса.

Чем rate отличается от burst в limit_req?

Rate задаёт устойчивую частоту обработки для одного ключа, а burst — размер допустимой очереди или краткого всплеска. Параметр nodelay пропускает burst сразу, но не отменяет общий предел.

Почему нельзя включать лимит по remote_addr за CDN без real IP?

Nginx увидит адрес CDN и объединит множество посетителей в один ключ. Сначала настройте доверенные сети и восстановление исходного адреса, затем проверьте журнал.