Модули
Nginx реализует алгоритм «дырявого ведра» (leaky bucket). Каждый запрос от клиента добавляет единицу в виртуальное ведро, которое опустошается с фиксированной скоростью. Если ведро переполнено — запрос отклоняется.
Конкретнее: при rate
Директива
Параметры:
В качестве ключа можно использовать не только IP. Например,
Директива
Типичная комбинация для API:
Без
Модуль
Здесь клиенту разрешено не более 5 одновременных соединений к
По умолчанию при срабатывании ограничения Nginx возвращает 503 Service Temporarily Unavailable. Это не всегда корректно с точки зрения семантики HTTP. Для перебора паролей логичнее вернуть 429 Too Many Requests.
Уровень логирования по умолчанию —
При
Один запрос в секунду на IP, всплеск до 6 попыток (1 + burst 5) проходит без задержки. Седьмая попытка в ту же секунду — 429. Для брутфорса с перебором словаря это серьёзное замедление.
Если заголовок
Не более двух параллельных загрузок на IP, каждая не быстрее 1 МБ/с.
Зона объявлена, но не подключена.
Слишком маленький размер зоны. Если зона заполняется, Nginx не может создать новое состояние для нового ключа и возвращает 503 всем. При большом количестве уникальных IP увеличивайте размер:
Ограничение по
Забытый
Конфликт с
После применения конфигурации:
При
Для более сложных сценариев — адаптивные лимиты, приоритизация по типу клиента, интеграция с WAF — смотрите в сторону Nginx как reverse proxy: проксирование запросов к backend-сервисам с правильной передачей заголовков Nginx как reverse proxy с передачей заголовков и связки с внешними сервисами.
ngx_http_limit_req_module и ngx_http_limit_conn_module встроены в Nginx по умолчанию и не требуют дополнительных пакетов. Они решают две разные задачи: limit_req ограничивает частоту запросов от одного клиента, limit_conn — количество одновременных соединений. Вместе они закрывают основные сценарии: перебор паролей, агрессивный парсинг, всплески трафика от ботов и исчерпание пула соединений к backend.Как работает leaky bucket
Nginx реализует алгоритм «дырявого ведра» (leaky bucket). Каждый запрос от клиента добавляет единицу в виртуальное ведро, которое опустошается с фиксированной скоростью. Если ведро переполнено — запрос отклоняется.
Конкретнее: при rate
10r/s Nginx пропускает один запрос каждые 100 мс. Если запросы приходят чаще, они либо ставятся в очередь (при наличии burst), либо сразу получают отказ. Очередь тоже ограничена — её размер задаётся параметром burst.limit_req_zone: определение зоны
Директива
limit_req_zone объявляет область в разделяемой памяти, где хранятся счётчики. Размещается в контексте http.
NGINX:
http {
limit_req_zone $binary_remote_addr zone=req_per_ip:10m rate=10r/s;
}
Параметры:
| Параметр | Назначение |
|---|---|
$binary_remote_addr | Ключ, по которому ведётся подсчёт. Бинарное представление IP занимает 4 байта (IPv4) или 16 байт (IPv6), что экономит память по сравнению с $remote_addr. |
zone=req_per_ip:10m | Имя зоны и размер разделяемой памяти. 1 МБ хранит примерно 16 000 состояний. |
rate=10r/s | Скорость «утечки». Допустимы r/s (запросы в секунду) и r/m (запросы в минуту). |
В качестве ключа можно использовать не только IP. Например,
$server_name для ограничения на весь виртуальный хост, $http_x_api_key для ограничения по API-ключу или комбинацию через конкатенацию.limit_req: применение к location
Директива
limit_req подключает зону к конкретному контексту (server, location, limit_except).
NGINX:
location /api/ {
limit_req zone=req_per_ip burst=20 nodelay;
proxy_pass http://backend;
}
burst и nodelay
burst=20— размер очереди. Если запросы приходят быстрее rate, до 20 лишних запросов будут поставлены в очередь и обработаны позже, с задержкой.
nodelay— запросы из очереди обрабатываются сразу, без искусственной задержки. Безnodelayклиент получает ответы с интервалом, соответствующим rate, даже если backend способен ответить быстрее.
Типичная комбинация для API:
rate=10r/s burst=20 nodelay. Это означает: устойчивый поток — 10 запросов в секунду, кратковременный всплеск до 30 запросов (10 + 20 из burst) проходит без задержки, всё сверх — отклоняется.Без
burst любой запрос сверх rate немедленно получает ошибку. Без nodelay burst-запросы обрабатываются по одному каждые 100 мс (при rate=10r/s), что увеличивает latency для клиента.limit_conn_zone и limit_conn
Модуль
limit_conn ограничивает число одновременных соединений, а не частоту запросов. Это полезно, когда один клиент открывает много параллельных соединений и исчерпывает worker-ресурсы.
NGINX:
http {
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
}
server {
location /downloads/ {
limit_conn conn_per_ip 5;
## ...
}
}
Здесь клиенту разрешено не более 5 одновременных соединений к
/downloads/. Шестое и последующие получат отказ.limit_conn можно применять несколько раз в одном location с разными зонами — например, одновременно ограничить по IP и по $server_name.Коды ответа и логирование
По умолчанию при срабатывании ограничения Nginx возвращает 503 Service Temporarily Unavailable. Это не всегда корректно с точки зрения семантики HTTP. Для перебора паролей логичнее вернуть 429 Too Many Requests.
NGINX:
limit_req_status 429;
limit_conn_status 429;
Уровень логирования по умолчанию —
error. Если лимиты срабатывают часто и засоряют error-лог, можно понизить:
NGINX:
limit_req_log_level warn;
limit_conn_log_level warn;
При
limit_req_log_level info события попадают только в access-лог, что удобно для мониторинга без алертов.Практические сценарии
Защита формы логина от перебора
NGINX:
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
server {
location /login {
limit_req zone=login burst=5 nodelay;
limit_req_status 429;
proxy_pass http://app;
}
}
Один запрос в секунду на IP, всплеск до 6 попыток (1 + burst 5) проходит без задержки. Седьмая попытка в ту же секунду — 429. Для брутфорса с перебором словаря это серьёзное замедление.
Ограничение API с дифференциацией по ключу
NGINX:
map $http_x_api_key $api_limit_key {
default $binary_remote_addr;
~.+ $http_x_api_key;
}
limit_req_zone $api_limit_key zone=api:20m rate=30r/s;
location /api/v1/ {
limit_req zone=api burst=50 nodelay;
proxy_pass http://api_backend;
}
Если заголовок
X-API-Key присутствует — лимит считается по ключу. Если нет — по IP. Это позволяет авторизованным клиентам получать более высокий лимит, а анонимный трафик ограничивать по адресу.Ограничение одновременных загрузок
NGINX:
limit_conn_zone $binary_remote_addr zone=dl:10m;
location /files/ {
limit_conn dl 2;
limit_rate 1m; # дополнительно: ограничение скорости отдачи
}
Не более двух параллельных загрузок на IP, каждая не быстрее 1 МБ/с.
Типичные ошибки
Зона объявлена, но не подключена.
limit_req_zone без соответствующей limit_req в location не делает ничего. Это самая частая причина «не работает».Слишком маленький размер зоны. Если зона заполняется, Nginx не может создать новое состояние для нового ключа и возвращает 503 всем. При большом количестве уникальных IP увеличивайте размер:
zone=req_per_ip:50m.burst=0 (по умолчанию) без понимания последствий. Без burst любой запрос сверх rate отклоняется мгновенно. Для страниц с параллельной загрузкой ресурсов (CSS, JS, изображения) это приводит к массовым 503 для легитимных пользователей. Ставьте burst хотя бы 10–20 для обычных страниц.Ограничение по
$remote_addr вместо $binary_remote_addr. Строковое представление IP занимает больше памяти. При 10 МБ зоны разница в количестве хранимых состояний — примерно в 2–4 раза.Забытый
limit_req_status. Клиенты и мониторинг видят 503, который обычно означает проблему с backend, а не rate limit. Это усложняет диагностику.Конфликт с
proxy_cache_lock или limit_conn на уровне upstream. Если backend тоже ограничивает соединения, двойное ограничение может дать непредсказуемое поведение. Проверяйте суммарные лимиты.Проверка результата
После применения конфигурации:
- Синтаксис:
nginx -t— убедитесь, что нет ошибок парсинга.
- Перезагрузка:
nginx -s reloadилиsystemctl reload nginx.
- Функциональный тест: отправьте серию запросов быстрее rate и убедитесь, что лишние получают 429 (или 503, если статус не переопределён).
Bash:
for i in $(seq 1 20); do
curl -s -o /dev/null -w "%{http_code}\n" http://example.com/login
done
При
rate=1r/s burst=5 nodelay первые 6 запросов вернут 200, остальные — 429.- Логи: проверьте error-лог на наличие строк
limiting requestsилиlimiting connections. Если их нет при нагрузке — зона не подключена или rate слишком высокий.
- Мониторинг в продакшене: добавьте в access-лог переменную
$limit_req_status(доступна с Nginx 1.17.1), чтобы видеть, какие запросы были ограничены, без парсинга error-лога.
NGINX:
log_format rate '$remote_addr [$time_local] "$request" $status '
'limit_req=$limit_req_status limit_conn=$limit_conn_status';
access_log /var/log/nginx/rate.log rate;
Ограничения модулей
limit_reqиlimit_connработают на уровне одного процесса Nginx. В кластере из нескольких серверов за балансировщиком каждый инстанс считает независимо. Для глобального лимита нужна внешняя система (например, Redis + Lua черезngx_http_lua_moduleили Nginx Plus сzone sync).
- Модули не различают «хороших» и «плохих» ботов. Если нужно пропустить поисковых crawlers без ограничений, добавьте
geoилиmapс белым списком и используйте переменную-ключ, которая для них возвращает пустую строку (пустой ключ отключает подсчёт).
limit_connсчитает соединения на этапе accept, до обработки заголовков. Это значит, что медленные соединения (slowloris) тоже занимают слот.
Для более сложных сценариев — адаптивные лимиты, приоритизация по типу клиента, интеграция с WAF — смотрите в сторону Nginx как reverse proxy: проксирование запросов к backend-сервисам с правильной передачей заголовков Nginx как reverse proxy с передачей заголовков и связки с внешними сервисами.
