Защита nginx от ботов, сканеров и паразитного трафика
Как только сайт появляется в сети, к нему начинают ломиться сканеры: ищут /wp-login.php, /.env, /admin, дырявые PHP-скрипты. Плюс агрессивные парсеры выжирают ресурсы. Nginx умеет отбивать это сам — rate limiting, фильтры по User-Agent и связка с fail2ban. Настроим оборону по слоям.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Слой 1: rate limiting
Ограничиваем частоту запросов с одного IP. В http-секции /etc/nginx/nginx.conf объявляем зоны:
http {
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
}
Применяем в server/location. Для обычных страниц мягкий лимит с burst, для форм логина — жёсткий:
location / {
limit_req zone=general burst=20 nodelay;
limit_conn conn 10;
}
location = /wp-login.php {
limit_req zone=login burst=3 nodelay;
}
burst — очередь всплеска, nodelay отдаёт их сразу, но сверх лимита возвращает 503. Перегрузить сервер уже не выйдет.
Ключевой момент — переменная $binary_remote_addr вместо $remote_addr: она хранит IP в бинарном виде и занимает меньше памяти зоны. Зона размером 10m вмещает около 160 тысяч уникальных адресов — этого хватит любому среднему сайту. Если сервер за реверс-прокси или CDN, используйте $http_x_forwarded_for, иначе все запросы придут с одного IP балансировщика и лимит сработает по всем сразу.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.
Арендовать VPS под защищённый сайтСлой 2: блокировка сканеров по User-Agent и путям
Большинство сканеров палятся по User-Agent или лезут по типовым путям. Режем их до обработки бэкендом:
map $http_user_agent $bad_bot {
default 0;
~*(nikto|sqlmap|nmap|masscan|zgrab|nuclei) 1;
"" 1;
}
server {
if ($bad_bot) { return 444; }
location ~* /\.(env|git|htaccess|aws) { deny all; return 404; }
location ~* (wp-config|xmlrpc|phpmyadmin) { deny all; return 404; }
}
Код 444 — фирменный nginx: рвёт соединение без ответа, экономит трафик и не даёт боту сигнала. Пустой User-Agent тоже блокируем — легальные клиенты его почти всегда заполняют.
Директиву map объявляют в http-секции, а проверку Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП. Большинство сканеров палятся по User-Agent или лезут по типовым путям. Режем их до обработки бэкендом: Код 444 — фирменный nginx: рвёт соединение без ответа, экономит трафик и не даёт боту сигнала. Пустой User-Agent тоже блокируем — легальные клиенты его почти всегда заполняют. Директиву map объявляют в http-секции, а проверку Rate limit отбивает всплеск, но упорный сканер долбит часами. fail2ban читает логи nginx и банит IP в фаерволе. Ставим и создаём фильтр: Джейл в /etc/fail2ban/jail.local: Если аудитория из конкретных стран, отсекаем остальные модулем geo или ngx_http_geoip2. Плюс режем очевидные атаки на переполнение: Короткие таймауты убивают медленные атаки типа Slowloris, когда бот держит соединение открытым по байту в секунду. Симулируем нагрузку и смотрим, что лимиты срабатывают: Вся эта оборона эффективна ровно настолько, насколько быстр диск и CPU: fail2ban парсит логи, nginx считает лимиты в памяти. На VPS MAATRIX с AMD EPYC + NVMe от $8/мес логи пишутся и читаются мгновенно, а root-доступ даёт полную свободу настроить фаервол и модули. Локации UK, США и РФ, оплата картой РФ, СБП, криптой или токеном MAAT — зарубежный сервер, доступный из России. Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.if ($bad_bot) — внутри server. Не увлекайтесь блокировкой по User-Agent: легитимные боты Google, Yandex и Bing тоже приходят с характерными строками, и их резать нельзя, иначе выпадете из поиска. Блокируйте только явные инструменты атак (sqlmap, nikto, nuclei) и оставляйте белый список для поисковиков. Проверить, кто именно к вам ходит, помогает разбор логов: awk '{print .Нужен сервер под эту задачу?
Слой 2: блокировка сканеров по User-Agent и путям
map $http_user_agent $bad_bot {
default 0;
~*(nikto|sqlmap|nmap|masscan|zgrab|nuclei) 1;
"" 1;
}
server {
if ($bad_bot) { return 444; }
location ~* /\.(env|git|htaccess|aws) { deny all; return 404; }
location ~* (wp-config|xmlrpc|phpmyadmin) { deny all; return 404; }
}if ($bad_bot) — внутри server. Не увлекайтесь блокировкой по User-Agent: легитимные боты Google, Yandex и Bing тоже приходят с характерными строками, и их резать нельзя, иначе выпадете из поиска. Блокируйте только явные инструменты атак (sqlmap, nikto, nuclei) и оставляйте белый список для поисковиков. Проверить, кто именно к вам ходит, помогает разбор логов: awk '{print $12}' access.log | sort | uniq -c | sort -rn | head.Слой 3: fail2ban против упорных
apt update && apt install -y fail2ban
nano /etc/fail2ban/filter.d/nginx-badbot.conf[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD) .*(wp-login|xmlrpc|\.env|phpmyadmin).*" (403|404|444)
ignoreregex =[nginx-badbot]
enabled = true
port = http,https
filter = nginx-badbot
logpath = /var/log/nginx/access.log
maxretry = 3
findtime = 300
bantime = 86400systemctl restart fail2ban
fail2ban-client status nginx-badbotСлой 4: гео-фильтр и лимит размеров
client_max_body_size 10m;
client_body_timeout 10s;
client_header_timeout 10s;
large_client_header_buffers 2 1k;Проверка и роль хостинга
ab -n 200 -c 20 https://ваш-домен/
tail -f /var/log/nginx/access.log | grep ' 444 'Нужен сервер под эту задачу?
Слой 3: fail2ban против упорных
Rate limit отбивает всплеск, но упорный сканер долбит часами. fail2ban читает логи nginx и банит IP в фаерволе. Ставим и создаём фильтр:
apt update && apt install -y fail2ban
nano /etc/fail2ban/filter.d/nginx-badbot.conf
[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD) .*(wp-login|xmlrpc|\.env|phpmyadmin).*" (403|404|444)
ignoreregex =
Джейл в /etc/fail2ban/jail.local:
[nginx-badbot]
enabled = true
port = http,https
filter = nginx-badbot
logpath = /var/log/nginx/access.log
maxretry = 3
findtime = 300
bantime = 86400
systemctl restart fail2ban
fail2ban-client status nginx-badbot
Слой 4: гео-фильтр и лимит размеров
Если аудитория из конкретных стран, отсекаем остальные модулем geo или ngx_http_geoip2. Плюс режем очевидные атаки на переполнение:
client_max_body_size 10m;
client_body_timeout 10s;
client_header_timeout 10s;
large_client_header_buffers 2 1k;
Короткие таймауты убивают медленные атаки типа Slowloris, когда бот держит соединение открытым по байту в секунду.
Проверка и роль хостинга
Симулируем нагрузку и смотрим, что лимиты срабатывают:
ab -n 200 -c 20 https://ваш-домен/
tail -f /var/log/nginx/access.log | grep ' 444 '
Вся эта оборона эффективна ровно настолько, насколько быстр диск и CPU: fail2ban парсит логи, nginx считает лимиты в памяти. На VPS MAATRIX с AMD EPYC + NVMe от $8/мес логи пишутся и читаются мгновенно, а root-доступ даёт полную свободу настроить фаервол и модули. Локации UK, США и РФ, оплата картой РФ, СБП, криптой или токеном MAAT — зарубежный сервер, доступный из России.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.
Арендовать VPS под защищённый сайтОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Rate limit не отрежет реальных пользователей?
При правильном burst — нет. Начните с rate=10r/s и burst=20, смотрите логи на 503 и корректируйте под свой трафик.
Чем 444 лучше 403?
444 закрывает соединение без ответа: боту не за что зацепиться, а вы экономите трафик и CPU. 403 всё же генерирует HTTP-ответ.
Нужен ли Cloudflare, если есть эти правила?
Для мелких и средних проектов nginx + fail2ban закрывают почти всё. CDN нужен против крупных DDoS, но базовую гигиену он не заменяет.