DDoS-атаки не всегда требуют сложной инфраструктуры. Иногда достаточно большого количества одновременных соединений или большого числа запросов к серверу, чтобы исчерпать доступные ресурсы. Один из способов снизить такой риск — настроить ограничения на уровне Nginx.
В этой статье разберём, как работают лимиты подключений в Nginx, какие директивы использовать и как настроить их так, чтобы защита не мешала обычным пользователям.
Зачем ограничивать подключения
Каждое соединение с сервером потребляет определённое количество ресурсов: память, файловые дескрипторы, сетевые ресурсы и время CPU. Если злоумышленник создаёт тысячи соединений одновременно, сервер может начать испытывать дефицит ресурсов ещё до того, как приложение получит существенную нагрузку.
Nginx позволяет ограничить количество одновременных соединений с одного IP-адреса. Это не является полноценной защитой от DDoS, но помогает отсеивать часть простых атак и ограничивать влияние одного источника трафика.
Основная директива для этой задачи — limit_conn.
Как работает limit_conn
Сначала нужно создать зону, в которой Nginx будет хранить информацию о клиентах:
http {
limit_conn_zone $binary_remote_addr zone=perip:10m;
server {
...
}
}
Здесь:
$binary_remote_addr— IP-адрес клиента в компактном бинарном формате;perip— имя зоны;10m— объём памяти, выделенный под эту зону.
Затем ограничение подключений применяется с помощью limit_conn:
server {
limit_conn perip 20;
location / {
proxy_pass http://backend;
}
}
В данном случае Nginx разрешает одному IP-адресу иметь не более 20 одновременно учитываемых подключений.
Если лимит превышен, Nginx отклоняет новые подключения с соответствующим статусом.
Почему используется $binary_remote_addr
Вместо $remote_addr обычно рекомендуется использовать $binary_remote_addr.
Оба варианта позволяют идентифицировать клиента по IP, но бинарное представление занимает меньше памяти. Это особенно важно, когда таблица клиентов должна хранить большое количество адресов.
Для IPv4 и IPv6 размер ключа остаётся небольшим, поэтому $binary_remote_addr хорошо подходит для зон ограничения.
Ограничение не равно rate limiting
Важно различать два механизма:
limit_connограничивает одновременные подключения;limit_reqограничивает частоту обработки запросов.
Например, клиент может установить несколько соединений и отправлять через них большое количество HTTP-запросов. В такой ситуации одного limit_conn может быть недостаточно.
Для ограничения частоты запросов используется limit_req:
http {
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=5r/s;
server {
location / {
limit_req zone=req_limit burst=10 nodelay;
proxy_pass http://backend;
}
}
}
Здесь базовая скорость составляет 5 запросов в секунду на IP. Параметр burst позволяет временно принять всплеск запросов, не блокируя клиента сразу.
На практике limit_conn и limit_req часто применяют совместно.
Пример базовой конфигурации
Для обычного веб-сервера можно начать с умеренных ограничений:
http {
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
server {
listen 80;
server_name example.com;
limit_conn perip 20;
location / {
limit_req zone=req_limit burst=20 nodelay;
proxy_pass http://backend;
}
}
}
Такая конфигурация ограничивает одновременно открытые соединения и скорость запросов с одного IP.
Однако универсальных значений 20 или 10r/s не существует. Допустимые параметры зависят от приложения, характера трафика и количества пользователей.
Осторожно с NAT и прокси
Ограничение по IP имеет важный недостаток: несколько реальных пользователей могут выглядеть для сервера как один IP-адрес.
Например, сотрудники компании могут выходить в интернет через единый NAT. То же самое возможно в некоторых мобильных сетях и публичных Wi-Fi.
Если установить слишком маленький лимит, защита начнёт блокировать не атакующего, а обычных пользователей.
Поэтому значения нужно подбирать на основании реального трафика и проверять по логам.
А если перед Nginx стоит балансировщик?
Если Nginx находится за CDN, reverse proxy или балансировщиком, $remote_addr может содержать адрес промежуточного сервера, а не исходного клиента.
В такой архитектуре необходимо корректно настроить восстановление исходного IP, например через real_ip:
http {
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
limit_conn_zone $binary_remote_addr zone=perip:10m;
}
Но диапазоны в set_real_ip_from нельзя копировать бездумно. В них должны находиться только доверенные прокси или балансировщики. Иначе злоумышленник сможет подменять заголовок с IP-адресом.
Как подобрать лимит
Лучше не начинать с максимально жёстких значений. Сначала стоит определить нормальное поведение приложения.
Полезно посмотреть:
- сколько одновременных соединений обычно создаёт один клиент;
- какие URL получают наибольшую нагрузку;
- как часто пользователи отправляют запросы;
- сколько соединений выдерживает backend;
- какие пики наблюдаются во время обычной работы.
После этого можно установить ограничение с запасом и постепенно корректировать его.
Для публичного API лимиты обычно должны быть более продуманными, чем для обычного сайта. Для API также имеет смысл учитывать токены, пользователей или другие идентификаторы, а не только IP.
Что делать с ошибками лимита
При превышении ограничений Nginx может возвращать ошибку клиенту. При необходимости её можно обработать отдельно:
limit_conn_status 429;
limit_req_status 429;
Код 429 Too Many Requests хорошо подходит для ситуации, когда клиент превысил установленное ограничение.
При этом важно понимать, что изменение HTTP-кода само по себе не усиливает защиту. Оно лишь делает реакцию сервера более понятной для клиента и приложений.
Как проверить конфигурацию
Перед применением изменений стоит проверить синтаксис:
nginx -t
Если проверка прошла успешно, конфигурацию можно перечитать без полной остановки сервера:
nginx -s reload
После этого следует проверить логи и убедиться, что реальные пользователи не начали получать ошибки из-за слишком строгих ограничений.
Чего Nginx не сможет сделать в одиночку
Лимиты подключений — это защитный слой, а не полноценная DDoS-защита.
Если атака создаёт огромный объём трафика, сетевой канал или инфраструктура перед сервером могут быть перегружены ещё до того, как запросы попадут в Nginx. В таком случае локальные лимиты уже не спасут.
Для крупных атак обычно нужны дополнительные уровни защиты:
- CDN и reverse proxy;
- фильтрация трафика на уровне сети;
- специализированные DDoS-защитные сервисы;
- rate limiting на API Gateway или балансировщике;
- мониторинг и автоматическое реагирование.
Главная задача Nginx в такой схеме — не заменить внешнюю DDoS-защиту, а уменьшить нагрузку на приложение и сделать инфраструктуру устойчивее к всплескам запросов.
Итог
limit_conn — простой и полезный механизм Nginx для ограничения количества одновременных подключений. В сочетании с limit_req он позволяет защитить backend от части чрезмерной активности на HTTP-уровне.
При настройке важно помнить о трёх вещах:
- лимит подключений и лимит запросов решают разные задачи;
- ограничение по IP может затронуть нескольких реальных пользователей за NAT;
- Nginx не способен самостоятельно остановить крупную DDoS-атаку, если перегружается сетевой канал.
Поэтому оптимальная стратегия — использовать лимиты Nginx как один из уровней многоуровневой защиты, а значения подбирать на основе реальной статистики нагрузки.



