Скрипт, который банит IP после условных 50 соединений, не является полноценной DDoS-защитой. Он может помочь против примитивного источника, но распределённая атака приходит с множества адресов и часто насыщает канал раньше, чем запросы добираются до Nginx.

1. Ограничьте частоту тяжёлых запросов
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend;
}
}Nginx использует алгоритм leaky bucket. Лимит нужно подбирать по реальному профилю трафика и сначала проверять в dry-run или на тестовом окружении, иначе можно заблокировать нормальных пользователей.
2. Ограничьте одновременные соединения
limit_conn_zone $binary_remote_addr zone=perip:10m;
server {
limit_conn perip 30;
}Это дополнительный слой, а не универсальная защита. Пользователи за NAT могут делить один внешний IP, поэтому слишком жёсткий лимит создаст ложные блокировки.
3. Кешируйте то, что можно
Статические файлы должны отдаваться напрямую, а повторяемые ответы backend — через proxy cache или кеш приложения, если это безопасно для содержимого. Чем меньше запросов доходит до PHP/Node/базы, тем выше запас по нагрузке.
4. Вынесите объёмную атаку на edge
Если атака забивает интернет-канал или состоит из тысяч распределённых источников, локальный firewall уже не поможет. Нужен CDN/WAF, anti-DDoS провайдера или фильтрация выше по сети.
5. Смотрите не только на количество соединений
- RPS по location;
- время ответа backend;
- 5xx/499;
- CPU и load average;
- очередь соединений;
- сетевой трафик;
- задержки базы данных.
6. Не автоматизируйте вечный бан по одной метрике
Боты, NAT, мобильные сети и прокси делают IP слабым идентификатором. Для приложения лучше сочетать rate limits, WAF-правила, authentication throttling и временные блокировки с понятным TTL.