Защита сайта на Nginx от атак на уровне приложений (L7)

Опубликовано 21/08/2026 Обновлено 06/10/2026 Просмотров: 53
Sécurité VPS Linux Nginx DDoS

Не все атаки одинаковы. Объёмная атака пытается забить сетевой канал: это пробка на автомагистрали, и с ней ещё на подходе справляется защита By-Hoster, без каких-либо действий с вашей стороны.

Атака на уровне приложений, так называемая атака уровня 7, устроена иначе. Трафик остаётся умеренным, но каждый запрос дорого обходится серверу: поиск, страница входа, корзина. Для сети это обычные посетители. Характер трафика становится виден только на уровне вашего веб-сервера, поэтому действовать нужно именно там.

Как узнать, что на самом деле нагружает сервер?

Ничего не меняйте, пока не поймёте, что происходит. Настройка, выставленная наугад, часто блокирует ваших реальных посетителей, не мешая атакующему.

Самые активные IP-адреса:

awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

Самые запрашиваемые URL:

awk -F'"' '{print $2}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

Трафик за последнюю минуту, чтобы оценить интенсивность:

tail -n 100000 /var/log/nginx/access.log | awk '{print $4}' | uniq -c | tail -20

Вы ищете закономерность: горстку очень «болтливых» адресов, один URL, по которому бьют без остановки, одинаковый user-agent повсюду. Именно эта закономерность определит правило, а не наоборот.

Также проверьте, что на самом деле перегружено:

uptime
systemctl status php8.3-fpm --no-pager

Часто сдаёт не Nginx, а PHP или база данных за ним.

Как ограничить частоту запросов с одного адреса в Nginx?

Nginx умеет подсчитывать запросы с каждого адреса и сдерживать те, что превышают лимит. Зоны объявляются в блоке http (файл /etc/nginx/nginx.conf):

http {
    limit_req_zone  $binary_remote_addr zone=req_per_ip:10m rate=10r/s;
    limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
}

Затем применяются в блоке сайта:

server {
    limit_req  zone=req_per_ip burst=20 nodelay;
    limit_conn conn_per_ip 20;
}

Что это означает на практике:

Настройка Действие
rate=10r/s 10 запросов в секунду с одного адреса в обычном режиме
burst=20 допускает всплеск в 20 запросов сверх этого темпа
nodelay обслуживает всплеск сразу, а не растягивает его во времени
limit_conn 20 не более 20 одновременных подключений с одного адреса

Зона размером 10m запоминает около 160 000 адресов — этого более чем достаточно.

burst без nodelay ставит запросы в очередь: сайт начинает тормозить вместо того, чтобы отказывать. С nodelay обычный посетитель, загружающий страницу и двадцать её ресурсов, проходит без задержки, а настойчивый бот получает ошибку 503.

Строже защищайте действительно ресурсоёмкие URL, а не весь сайт:

location = /wp-login.php {
    limit_req zone=req_per_ip burst=3 nodelay;
}

Как проверить конфигурацию Nginx перед применением?

С помощью nginx -t, которая проверяет синтаксис, ничего не применяя, а затем горячей перезагрузки конфигурации вместо перезапуска.

nginx -t

Эта команда проверяет синтаксис, ничего не прерывая. Она должна ответить syntax is ok, а затем test is successful. Только после этого:

systemctl reload nginx

reload перезагружает конфигурацию, не обрывая текущие подключения, в отличие от restart. В разгар атаки это важно.

Следите за тем, что отклоняется:

grep "limiting requests" /var/log/nginx/error.log | tail -20

Если вы видите там свои собственные адреса или адреса своих клиентов, лимит слишком низкий. Повысьте его.

Как реже генерировать одну и ту же страницу?

Лучший способ выдержать атаку — часто не пересчитывать страницу при каждом запросе. Страница, закешированная на 60 секунд, почти ничего не стоит, сколько бы раз её ни запрашивали.

http {
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=site:50m inactive=10m;
}
location / {
    proxy_cache site;
    proxy_cache_valid 200 60s;
    proxy_cache_use_stale error timeout updating;
}

proxy_cache_use_stale — спасительная строка: если бэкенд перестаёт отвечать, Nginx продолжает отдавать последнюю известную версию вместо ошибки.

В WordPress, PrestaShop или Laravel модуль кеширования на уровне приложения даёт тот же эффект с меньшим риском побочных эффектов.

Как блокировать повторных нарушителей с помощью fail2ban?

Ограничение замедляет, блокировка останавливает. fail2ban читает журналы и добавляет правило брандмауэра против адресов, которые слишком часто упираются в лимит.

apt install fail2ban

В /etc/fail2ban/jail.local:

[nginx-limit-req]
enabled  = true
filter   = nginx-limit-req
logpath  = /var/log/nginx/error.log
maxretry = 10
findtime = 60
bantime  = 3600
systemctl restart fail2ban
fail2ban-client status nginx-limit-req

Если прочитать правило вслух: адрес, который 10 раз за 60 секунд упёрся в ограничение, блокируется на час.

Каких настроек следует избегать при атаке на уровне приложений?

Эти настройки широко распространены и приносят больше вреда, чем пользы.

Чего избегать Почему
Блокировать curl и wget по user-agent Атакующий меняет user-agent одной строкой. А вы ломаете свой мониторинг, вебхуки платёжных систем и скрипты резервного копирования.
client_max_body_size 1k Любая загрузка изображения, любая сколько-нибудь длинная форма, любое обновление CMS завершается ошибкой.
allow для подсети, а затем deny all Вы закрываете сайт для всего интернета, а значит, и для своих посетителей. Используйте эту схему только для URL администрирования.
Блокировать целые страны «на всякий случай» Много ложных срабатываний, а атакующие арендуют машины по всему миру.
Отключать журналы «ради производительности» Вы теряете единственный способ понять, что с вами происходит.

Простое правило: любая мера, блокирующая шире, чем наблюдаемая закономерность, обернётся потерей клиентов.

Когда нужно создать обращение к нам?

Когда проблема выходит за рамки того, что можно исправить на машине: перегруженный канал, продолжительная атака или тысячи адресов.

Создайте обращение, если:

  • машина остаётся недоступной, несмотря на эти настройки;
  • трафик перегружает сетевой канал ещё до того, как достигает Nginx;
  • атака продолжается и идёт с тысяч разных адресов.

Приложите к обращению фрагменты журналов из шага 1 и соответствующий временной интервал. Это позволит нам действовать на входе, на уровне сети, где вы вмешаться не можете.

Помогла ли вам эта статья?