ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Nginx 444 и 403: блокируем ботов и сканеры через map, geo, deny и return

Разбираем, когда выбирать nginx 444, а когда 403, и как собрать аккуратную схему блокировок на map и geo. Покажу deny и return 444, быстрый анализ access log и настройку set_real_ip_from, чтобы банить по реальному IP за прокси/CDN.
Nginx 444 и 403: блокируем ботов и сканеры через map, geo, deny и return

Почему именно 444 и 403: разные задачи, разный эффект

В Nginx есть два популярных способа «отрезать» нежелательный трафик на входе: вернуть клиенту HTTP-ответ (чаще всего 403) или молча разорвать соединение (нестандартный код 444, исторически из nginx).

403 — это корректный HTTP-ответ «доступ запрещён». Он полезен, когда вы хотите явно зафиксировать запрет (например, закрыть админку извне), а также когда вам важно, чтобы отказ был понятен в браузере, мониторинге и при отладке интеграций.

444 — это «ничего не отвечать и закрыть соединение». Для массовых сканеров и примитивных ботов это часто эффективнее: вы не тратите время на генерацию тела ответа и не даёте лишнего «сигнала», что ресурс живой и отвечает. В access log (если вы не меняли формат) это обычно отображается как 444 в поле $status, но на стороне клиента это будет именно обрыв соединения.

Практическое правило: 403 — для контролируемых запретов и прозрачной диагностики, 444 — для «мусорного» трафика (сканеры, агрессивные боты), когда важнее экономить ресурсы и не вступать в диалог.

Сначала диагностика: что именно блокируем (access log analysis)

Перед тем как писать правила map/geo/deny, полезно понять профиль «плохого» трафика. Иначе легко заблокировать полезных роботов (поисковики), аптайм-проверки, API-партнёров или пользователей за корпоративным NAT.

Ниже — несколько команд, которые дают быстрый срез по Nginx access log. Подставьте путь к своему логу (например, /var/log/nginx/access.log или отдельный лог виртуального хоста).

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

Топ IP по числу запросов — первая подсказка: боты часто «светятся» частотой и однотипными URI.

awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head

Топ URI помогает быстро увидеть типичный паттерн сканеров: /wp-login.php, /xmlrpc.php, /.env, /.git/, /phpmyadmin, /vendor/phpunit и другие «универсальные» пути.

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

Срез по User-Agent часто показывает «bad bots»: пустой UA, мусорные строки, массовые «python-requests», «curl», «Go-http-client». Сами по себе такие UA не всегда зло, но в связке с подозрительными URI и частотой — хороший сигнал.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Важно: банить нужно реальный IP (set_real_ip_from) за прокси/CDN

Если Nginx стоит за балансировщиком, reverse proxy или CDN, в логах и в $remote_addr вы можете видеть IP прокси, а не клиента. Тогда любые geo/deny по IP будут бессмысленны: вы либо не заблокируете атакующего, либо случайно «отрежете» весь трафик через прокси.

Решение — настроить Real IP модуль: принимать «истинный» адрес из заголовка (чаще X-Forwarded-For или X-Real-IP) только от доверенных источников.

http {
  set_real_ip_from 192.0.2.10;
  set_real_ip_from 198.51.100.0/24;
  real_ip_header X-Forwarded-For;
  real_ip_recursive on;
}

Где:

  • set_real_ip_from — список доверенных прокси/балансеров (IP или подсети), которым вы разрешаете «сообщать» реальный адрес.
  • real_ip_header — заголовок, из которого берём адрес клиента.
  • real_ip_recursive — корректная обработка цепочки X-Forwarded-For, если прокси несколько.

После этого $remote_addr станет «похож» на реальный IP клиента, а блокировки по IP начнут работать ожидаемо. Обязательно перепроверьте формат логов и убедитесь, что вы больше не анализируете IP прокси.

Пример настройки set_real_ip_from и real_ip_header в конфигурации Nginx

Быстрый блок по IP: deny + 403 (простой и надёжный)

Самый прямолинейный способ остановить конкретные IP/подсети — директива deny. Она возвращает 403. Это удобно, когда у вас есть подтверждённый список источников (например, IP, которые долбят админку, или подсеть, которая сканирует уязвимости).

server {
  listen 80;
  server_name example.com;

  location / {
    deny 203.0.113.55;
    deny 203.0.113.0/24;
    allow all;

    try_files $uri $uri/ =404;
  }
}

Плюсы: очевидно, быстро, читаемо. Минусы: deny не умеет условия по UA/URI и при большом списке хуже поддерживается (хотя десятки записей обычно не проблема).

«Тихий» бан: return 444 и где его ставить

Чтобы включить nginx 444, используют return 444;. Обычно его располагают как можно раньше — на уровне server (или раннего location), чтобы не тратить ресурсы на лишние проверки.

Пример «закрыть всё, кроме allowlist» как шаблон (в реальности так делают редко, но для понимания полезно):

server {
  listen 80;
  server_name example.com;

  if ($remote_addr != 198.51.100.25) { return 444; }

  location / {
    try_files $uri $uri/ =404;
  }
}

Директива if в Nginx — инструмент, с которым нужно быть аккуратным. На практике для условных блокировок надёжнее и масштабируемее использовать map и geo: они вычисляют переменные «без сюрпризов» и хорошо читаются.

map: блокируем по User-Agent и другим признакам

map — один из самых удобных инструментов для политики фильтрации: вы описываете условия, а результат складываете в переменную (например, $block_ua). Затем в server делаете одно короткое решение: return 444 или return 403.

Пример: переменная будет равна 1 для подозрительных User-Agent.

http {
  map $http_user_agent $block_ua {
    default 0;
    "" 1;
    ~*"python-requests" 1;
    ~*"masscan" 1;
    ~*"zgrab" 1;
    ~*"sqlmap" 1;
  }

  server {
    listen 80;
    server_name example.com;

    if ($block_ua) { return 444; }

    location / {
      try_files $uri $uri/ =404;
    }
  }
}

Здесь if используется в безопасной форме if (var) { return ... }. Обычно это не вызывает проблем, потому что не участвует в переписывании URI и не строит сложную ветвящуюся логику.

Практика: не блокируйте «curl» или «Go-http-client» автоматически. Эти UA часто используют легитимные проверки, интеграции, CI и некоторые SDK. Лучше блокировать по UA только в связке с подозрительными URI или другими сигналами.

map по URI: режем типовые пути сканеров

Большая часть block scanners решается точечным закрытием явно вредных маршрутов. Например, если у вас не WordPress, запросы к /wp-login.php и /xmlrpc.php почти всегда шум.

http {
  map $request_uri $block_uri {
    default 0;
    ~*"^/wp-login\.php" 1;
    ~*"^/xmlrpc\.php" 1;
    ~*"^/\.env$" 1;
    ~*"^/\.git/" 1;
    ~*"^/phpmyadmin" 1;
  }

  server {
    listen 80;
    server_name example.com;

    if ($block_uri) { return 444; }

    location / {
      try_files $uri $uri/ =404;
    }
  }
}

Выбор между return 444 и return 403 тут зависит от политики. Для «мусорных» URI чаще ставят 444. Для осознанно закрытых зон (например, /admin с внешнего мира) логичнее 403, чтобы при отладке было видно, что это именно запрет.

Если вы часто работаете с кэшем и диапазонными запросами, полезно учитывать, что часть «подозрительных» паттернов приходит от клиентов и плееров. В таких случаях помогает разнести правила по зонам и аккуратно смотреть на метрики. См. также: Range-запросы и кэш в Nginx/Apache: практические нюансы.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

geo: блокируем по IP и подсетям аккуратно и быстро

geo создаёт переменную на основании IP-адреса клиента. Это отличный вариант для ручного бана подсетей, allowlist офисных IP, ограничения доступа к админке, «мягкого» контроля партнёрских интеграций.

http {
  geo $block_ip {
    default 0;

    203.0.113.55 1;
    203.0.113.0/24 1;
  }

  server {
    listen 80;
    server_name example.com;

    if ($block_ip) { return 403; }

    location / {
      try_files $uri $uri/ =404;
    }
  }
}

Почему иногда лучше 403 вместо 444 при блоке по IP? Потому что IP-блоки часто «операционные»: вы будете разбираться, не задели ли вы пользователя или партнёра. Понятный HTTP-ответ ускоряет диагностику.

Комбинируем map + geo: один флаг блокировки и единая точка return

Когда правил становится много, главная цель — не превратить конфиг в набор разрозненных «заплаток». Удобный приём: собрать общий флаг $block из нескольких источников, а затем сделать ровно одно решение на входе.

http {
  map $http_user_agent $block_ua {
    default 0;
    "" 1;
    ~*"zgrab" 1;
    ~*"sqlmap" 1;
  }

  map $request_uri $block_uri {
    default 0;
    ~*"^/\.env$" 1;
    ~*"^/\.git/" 1;
  }

  geo $block_ip {
    default 0;
    203.0.113.0/24 1;
  }

  map "$block_ua$block_uri$block_ip" $block {
    default 0;
    ~*"1" 1;
  }

  server {
    listen 80;
    server_name example.com;

    if ($block) { return 444; }

    location / {
      try_files $uri $uri/ =404;
    }
  }
}

Идея простая: map и geo вычисляют «0/1», а итоговое решение централизовано. Такой подход легче сопровождать, переносить между проектами и откатывать.

403 для админки, 444 для мусора: типовой практичный сценарий

Частая задача: админка доступна только с корпоративных IP, всё остальное — 403 (с понятной диагностикой), а мусорные сканы по типовым путям — 444 на входе.

http {
  geo $admin_allow {
    default 0;
    198.51.100.10 1;
    198.51.100.0/24 1;
  }

  server {
    listen 80;
    server_name example.com;

    location ^~ /admin/ {
      if ($admin_allow = 0) { return 403; }
      proxy_pass http://backend;
    }

    location / {
      try_files $uri $uri/ =404;
    }
  }
}

Тут 403 оправдан: это «намеренно закрытая» зона, и по ответу проще отличить запрет от проблем проксирования.

Анализ access log: топ IP, URI и User-Agent для выявления сканеров и ботов

Логи и наблюдаемость: не потерять сигнал в шуме

Когда вы вводите return 444, часть инструментов аналитики будет интерпретировать это как сетевую ошибку. Это нормально, но важно не перепутать такую «ошибку для бота» с реальными проблемами доступности.

Что обычно помогает на практике:

  • Помечать отказы отдельной переменной и выводить её в access log, чтобы быстро отделять «наши блокировки» от проблем приложения.
  • Следить за долей 444/403 во времени: резкий рост часто означает новую волну сканирования.
  • После настройки set_real_ip_from проверить, что в логах действительно клиентские IP, иначе статистика по источникам будет «плоской» (один IP прокси).

Проверка и безопасное внедрение

Любые правила блокировки лучше выкатывать итеративно:

  1. Сначала добавить правила в «наблюдательном» режиме: не блокировать, а только помечать переменную и писать её в лог.
  2. Проверить 24–72 часа, что не попали легитимные клиенты и боты.
  3. Включить блок (return 444 или return 403) и продолжать мониторить.

После изменений всегда прогоняйте синтаксическую проверку и перечитывание конфига:

nginx -t
systemctl reload nginx

Типичные ошибки и как их избежать

Блокировка по IP без realip

Самая частая причина «почему deny/geo не работает»: вы баните IP прокси. Настройте set_real_ip_from и только потом добавляйте IP-правила.

Слишком агрессивный блок по User-Agent

Под «bad bots» легко случайно записать всё подряд. Делайте исключения для своих интеграций и мониторинга, а блокировку по UA используйте как один из сигналов, а не единственный.

Смешивание логики по всему конфигу

Когда return/deny раскиданы по десяткам location, сопровождение становится дорогим. Централизуйте решение через map/geo и один флаг $block.

Короткий итог

nginx 403 подходит для управляемых запретов и понятной диагностики. nginx 444 — эффективный способ «не разговаривать» со сканерами и снизить лишнюю нагрузку. Для аккуратной политики блокировок лучше всего работает связка map (условия по UA/URI/заголовкам) + geo (условия по IP) + единая точка решения через return 444 или return 403. И не забывайте про set_real_ip_from: без него любые IP-блокировки за прокси и CDN легко превращаются в фикцию.

Если вам нужно быстро поднять изолированную конфигурацию Nginx с гибкими правилами фильтрации и логированием, обычно удобнее делать это на VDS, чтобы иметь полный контроль над сетевыми настройками и конфигами.

Поделиться статьей

Вам будет интересно

Kubernetes PV/PVC: Pending, FailedMount, ReadOnly и застрявшие finalizers — практический разбор OpenAI Статья написана AI (GPT 5)

Kubernetes PV/PVC: Pending, FailedMount, ReadOnly и застрявшие finalizers — практический разбор

Если PVC завис в Pending, поды получают FailedMount, том внезапно становится ReadOnly или удаление PV/PVC висит на finalizers — пр ...
SSH Permission denied (publickey): проверяем sshd_config и ~/.ssh без лишней боли OpenAI Статья написана AI (GPT 5)

SSH Permission denied (publickey): проверяем sshd_config и ~/.ssh без лишней боли

Permission denied (publickey) обычно сводится к трём причинам: клиент отправляет не тот ключ, сервер не принимает ключевую аутенти ...
systemd restart loop: как остановить бесконечные рестарты и настроить Restart/StartLimitBurst OpenAI Статья написана AI (GPT 5)

systemd restart loop: как остановить бесконечные рестарты и настроить Restart/StartLimitBurst

Если сервис в systemd уходит в бесконечный перезапуск, обычно виноваты неверный ExecStart, права/пользователь, неподходящий Type= ...