Защита от ботов в Nginx: рабочие приёмы без перегибов
Nginx может отсечь простой скрейпер, ограничить частоту запросов и не дать одному адресу занять все соединения. Но грубая блокировка User-Agent ломает API и мобильные приложения. Разбираем рабочую схему для Nginx 1.25–1.26.

Один скрипт, который отправляет по 20 запросов в секунду с каждого адреса, способен забить лимиты API, разогреть CPU и превратить страницу поиска в источник расходов. Простая блокировка по User-Agent не спасает: бот меняет заголовок за секунду, а под такой фильтр иногда попадают реальные клиенты. Для Nginx Open Source 1.25–1.26 практичнее собрать три слоя: ограничение частоты, лимит одновременных соединений и точечную фильтрацию явно мусорного трафика.
Что именно защищаем
Не ставьте один лимит на весь сайт. Главная страница, поиск, форма входа и API потребляют разное количество ресурсов. Лимит в 5 запросов в секунду может быть мягким для HTML, но слишком жёстким для поиска с несколькими AJAX-запросами. И наоборот: 30 запросов в секунду на логин уже создают удобный канал для перебора паролей.
Перед настройкой определите три группы маршрутов:
- дорогие: поиск, авторизация, оформление заказа, генерация отчётов;
- публичные: страницы каталога, изображения, статические файлы;
- служебные: API, webhook и административная панель.
Если сайт работает за CDN, балансировщиком или облачным прокси, сначала настройте передачу реального IP. Иначе Nginx увидит адрес прокси, и весь трафик пользователей попадёт в одну корзину. Директива set_real_ip_from должна содержать только доверенные сети вашего провайдера, а не произвольный диапазон.
Хорошая защита от ботов не угадывает, кто робот. Она ограничивает стоимость вредного действия и оставляет запас для нормального клиента.
[1] Rate limiting — ограничение частоты запросов. Nginx хранит счётчик для ключа, например IP-адреса, и задерживает или отклоняет запросы после достижения заданной скорости.
Приём 1. Ограничьте частоту запросов
Основной инструмент Nginx — модуль ngx_http_limit_req_module. Зона и скорость объявляются в контексте http, а применение лимита — внутри server или location. В качестве ключа используем бинарное представление IP: оно экономнее по памяти, чем строка адреса.
http {
# 10 МБ хватает примерно для большого числа адресов;
# точное количество зависит от версии и платформы.
limit_req_zone $binary_remote_addr zone=search_by_ip:10m rate=5r/s;
server {
listen 443 ssl;
server_name example.com;
location = /search {
limit_req zone=search_by_ip burst=10 nodelay;
limit_req_status 429;
proxy_pass http://search_backend;
}
}
}
rate=5r/s задаёт среднюю скорость для одного IP, а burst=10 разрешает короткий всплеск. Параметр nodelay пропускает запросы из очереди сразу, пока не исчерпан burst; после этого Nginx возвращает ошибку. Без nodelay лишние запросы будут постепенно задерживаться, что иногда создаёт длинные очереди и таймауты.
Ответ 429 лучше 403: клиент понимает, что нужно повторить запрос позже. Для API добавьте в документацию правило повторной попытки с увеличивающейся задержкой. Бесконечные автоматические повторы только усиливают атаку.
Приём 2. Ограничьте одновременные соединения
Лимит запросов не закрывает другую дыру: клиент может открыть множество соединений и держать их занятыми медленными запросами. Для этого есть limit_conn. Он считает активные соединения по выбранному ключу.
http {
limit_conn_zone $binary_remote_addr zone=connections_by_ip:10m;
server {
listen 443 ssl;
server_name example.com;
location / {
limit_conn connections_by_ip 20;
limit_conn_status 429;
proxy_pass http://app_backend;
}
}
}
Значение 20 — не универсальная рекомендация. Оно зависит от HTTP/1.1, HTTP/2, размера ответов и того, есть ли у пользователя корпоративный NAT. Если поставить лимит 2–3, один офис или мобильная сеть может начать получать ошибки для всех сотрудников. Для публичного сайта начните с режима наблюдения через логи и подберите число по пику активных соединений.
Учитывайте, что запросы к статическим файлам тоже могут занять соединения. Если Nginx сам отдаёт изображения и CSS, отдельный лимит для тяжёлого динамического маршрута обычно безопаснее, чем одинаковое правило на весь server.
Приём 3. Отдельный профиль для дорогих маршрутов
Бот редко ценит главную страницу. Ему нужны поиск, карточки с фильтрами, логин или массовая выгрузка. Снижайте скорость именно там, где запрос дорогой для backend.
| Участок | Что ограничивать | Цена ошибки | Практический подход |
|---|---|---|---|
| HTML и каталог | частоту запросов с IP | пользователь увидит 429 при резком переходе | мягкий лимит и небольшой burst |
| Поиск и фильтры | частоту и соединения | нагрузка на базу данных | отдельная зона с более низкой скоростью |
| Логин | частоту попыток | можно задеть общий NAT | малый лимит, но с учётом авторизованного пользователя на уровне приложения |
| API и webhook | частоту по IP или ключу клиента | поломка интеграции | лимит согласовать с владельцем API, возвращать 429 |
Если API использует ключи клиентов, IP-лимит в Nginx не заменяет квоту по ключу. Один адрес может обслуживать десятки клиентов, а один ключ — использоваться через несколько адресов. Разделение по токену безопаснее делать в приложении, где есть аутентификация и учёт тарифа.
Что делать с User-Agent и плохими адресами
User-Agent — сигнал, а не доказательство. Его можно подделать, а пустой или необычный заголовок встречается у мониторинга, мобильных приложений и внутренних интеграций. Поэтому не блокируйте весь трафик с curl или python-requests, если этими клиентами пользуются ваши разработчики и роботы мониторинга.
Точечное правило допустимо для заведомо лишнего сканера, который уже виден в логах. Оно должно быть коротким и обратимым:
http {
map $http_user_agent $is_obvious_scanner {
default 0;
~*"masscan|zgrab" 1;
}
server {
listen 443 ssl;
server_name example.com;
location / {
if ($is_obvious_scanner) {
return 403;
}
proxy_pass http://app_backend;
}
}
}
Такой фильтр не защищает от целевого бота и может устареть. Не добавляйте в него поисковых роботов только по названию: сначала проверьте IP, DNS и цель запросов. Списки адресов лучше поддерживать автоматически через внешний процесс и загружать в Nginx после проверки, а не редактировать конфигурацию вручную во время атаки.
Когда какой подход выбирать
Если нужно быстро снизить нагрузку
Начните с limit_req на одном дорогом маршруте. Это минимальное изменение и понятный сигнал в логах. Сначала включите limit_req_dry_run on в подходящем контексте, если эта директива доступна в вашей сборке, и посмотрите, сколько запросов было бы ограничено. После проверки уберите dry run и оставьте измерение в журнале.
Если сервер упирается в количество соединений
Добавьте limit_conn, но проверьте графики активных соединений, таймауты upstream и долю HTTP/2-трафика. Лимит соединений не заменяет настройку proxy_read_timeout: медленный backend и долгий ответ требуют отдельного разбора.
Если виден повторяющийся сканер
Используйте точечный map или список IP только после проверки логов. Цена неверной блокировки — потерянные пользователи, webhook и поисковая индексация. Цена слишком мягкого правила — несколько лишних запросов, поэтому автоматический 403 должен быть последним, а не первым шагом.
Что ломается чаще всего
- Все пользователи получают 429. В качестве ключа оказался адрес CDN или балансировщика. Проверьте цепочку real IP и список доверенных прокси.
- Конфигурация не проходит проверку. Зона объявлена не в
http, а внутриserver. Перенеситеlimit_req_zoneиlimit_conn_zoneна нужный уровень. - Приложение бесконечно повторяет запросы. Клиент не обрабатывает 429. Добавьте задержку, ограничение числа повторов и понятный заголовок или тело ответа на уровне API.
- После фильтрации пропал мониторинг. Проверяйте User-Agent и IP ваших health-check, прежде чем включать 403.
- Лимит почти не влияет на атаку. Бот распределяет запросы по множеству адресов. IP-ограничение не заменяет CAPTCHA, авторизацию, квоты и защиту на уровне CDN.
Проверяйте конфигурацию командой nginx -t, затем применяйте её штатной перезагрузкой. До и после изменения смотрите количество ответов 2xx, 4xx и 5xx, p95 задержки, активные соединения, нагрузку на upstream и число срабатываний лимита. Без этих метрик вы не отличите удачную защиту от случайного отключения части клиентов.
Что делать дальше
Соберите срез access log за обычный час и за пиковую нагрузку: URI, статус, IP после real IP, User-Agent, время ответа и размер ответа. Найдите пять самых дорогих маршрутов, включите для одного из них мягкий rate limit, а через день сравните p95 и количество 429. Затем добавьте лимит соединений и отдельно проверьте API. Кеширование, CAPTCHA и поведенческий анализ — следующие уровни, но их стоит подключать после того, как Nginx уже умеет ограничивать частоту и не ломает легитимный трафик.
Помощник по курсовой и диплому: Большая работа поэтапно: от плана до защиты
Спросить за 25 ₽Источники: Официальная документация Nginx: ngx_http_limit_req_module, Официальная документация Nginx: ngx_http_limit_conn_module
Частые вопросы
Можно ли защитить сайт от всех ботов только настройками Nginx?
Почему после limit_req реальные пользователи получают ошибку 429?
Можно ли блокировать ботов по User-Agent?
Что выбрать: limit_req или limit_conn?
Что делать, если бот использует тысячи IP-адресов?
Нужно ли ставить CAPTCHA на каждый ответ 429?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.