В середине октября 2026 года сообщество следило за публичными графиками Cloudflare Radar по сети Яндекса, AS13238. Метрика, которую Cloudflare публикует для этого ASN, классифицировала 99,6% HTTP-запросов как бот-трафик, а на Hacker News сообщество сообщило о резком падении этого объёма после заявленного удара по дата-центру Яндекса.[12] Какова бы ни была точная причина, сигнал тот же, что вы видите в своих логах доступа: заметная доля «бот-флуда» на публичный сайт приходит с небольшого числа сетей и краулеров и выглядит как запросы с узнаваемыми user-agent.

Управлять этими волнами в источнике вы не можете. Управлять можно только собственным edge. Если nginx работает у вас как reverse proxy перед приложением, у вас есть четыре реальных рычага, и три из них есть в stock-nginx:

  1. robots.txt для краулеров, которые сотрудничают.
  2. map + return 444 для жёсткой блокировки известных AI-краулеров по user-agent.
  3. limit_req для ограничения флуда, который подделывает или пропускает user-agent.
  4. TLS-фингерпринтинг (JA3/JA4) для краулеров, которые притворяются браузером. Это сложный уровень, и stock-nginx его не умеет (подробности ниже).

Этот гайд предполагает, что у вас уже есть рабочий reverse proxy на nginx; если нет, начните с нашего укреплённого конфига reverse proxy.

Знайте user-agent, которые блокируете

Прежде чем писать хоть одну директиву, поймите: «AI-краулер» это не одна сущность. Каждый вендор запускает несколько ботов с разными задачами, и блокировка не того бота лишит вас видимости в поисковом продукте, который вам, возможно, нужен. Вот токены, которые вы увидите в 2026 году, из документации самих вендоров:

Токен user-agent Оператор Для чего краулит
GPTBot OpenAI Контент для обучения генеративных foundation-моделей.[2]
OAI-SearchBot OpenAI Показ сайтов в результатах поиска ChatGPT; при отказе вы исчезаете из ответов поиска ChatGPT.[2]
ChatGPT-User OpenAI Загрузка страниц по запросу пользователя ChatGPT; инициировано пользователем, robots.txt может не применяться.[2]
ClaudeBot Anthropic Контент для обучения моделей Claude.[3]
Claude-SearchBot, Claude-User Anthropic Поисковая индексация и пользовательские загрузки; правила отдельные от ClaudeBot.[3]
Bytespider ByteDance Агрессивный веб-краулер, связанный с продуктами ByteDance.[7]
Amazonbot Amazon Улучшение продуктов Amazon; может использоваться для обучения AI-моделей Amazon.[4]
Google-Extended Google Не краулер. Токен robots.txt, управляющий обучением и grounding моделей Gemini, без влияния на Google Search.[5]
Meta-ExternalAgent Meta Обучение foundation-моделей AI и прямая индексация контента.[6]

Две вещи в этой таблице легко понять неправильно.

Google-Extended никогда не появляется в user-agent. Ни один запрос не содержит «Google-Extended» в заголовке User-Agent. Это отдельный product-токен, который вы кладёте в robots.txt: он говорит Google, что контент, скрауленный с вашего сайта, нельзя использовать для обучения будущих моделей Gemini или для grounding в Gemini Apps и Vertex AI, и Google прямо пишет, что это не влияет на включение сайта в Google Search и не является сигналом ранжирования.[5] Поэтому Google-Extended обрабатывается в robots.txt, а не в nginx-мапе.

Пользовательские фетчеры могут не соблюдать robots.txt. OpenAI документирует, что запросы ChatGPT-User инициируются пользователем и правила robots.txt к ним могут не применяться.[2] Amazon говорит то же про Amzn-User.[4] Это важно для уровня enforcement: вежливых контролей недостаточно.

Уровень 1: robots.txt, слой сотрудничества

robots.txt регулируется RFC 9309 (Robots Exclusion Protocol, стандарт IETF с 2022 года). Для этой задачи важны два свойства. Первое: это набор просьб, а не авторизация доступа: стандарт прямо говорит, что эти правила не являются формой контроля доступа, и любой краулер может их игнорировать.[1] Второе: матчинг идёт по product-токену: краулер находит группу, чья строка user-agent совпадает с его токеном, и выигрывает самое длинное совпадение пути, а сам /robots.txt разрешён всегда.[1]

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

# /var/www/example.com/robots.txt
User-agent: *
Allow: /

# AI-трейнинг и модельные краулеры: блокируем
User-agent: GPTBot
Disallow: /

User-agent: ClaudeBot
Disallow: /

User-agent: Bytespider
Disallow: /

User-agent: Amazonbot
Disallow: /

User-agent: Meta-ExternalAgent
Disallow: /

# Google-Extended это токен, а не краулер: отказ от обучения/grounding в Gemini
User-agent: Google-Extended
Disallow: /

# Поисковые боты, которые вам, возможно, нужны (правьте под себя):
User-agent: OAI-SearchBot
Allow: /

User-agent: Claude-SearchBot
Allow: /

Обратите внимание: решение блокировать OAI-SearchBot это бизнес-решение. OpenAI говорит, что сайты, отказавшиеся от OAI-SearchBot, не появятся в ответах поиска ChatGPT, хотя могут остаться как навигационные ссылки.[2] Хотите, чтобы поиск ChatGPT цитировал вас, оставляйте Allow.

Нестандартная директива, о которой стоит знать: Crawl-delay. Anthropic явно поддерживает её для своих ботов и документирует User-agent: ClaudeBot + Crawl-delay: 1 как способ замедлить краулинг без блокировки.[3] Amazonbot, напротив, директиву crawl-delay вообще не поддерживает.[4] Воспринимайте Crawl-delay как специфику вендора, а не универсальное правило.

Главная оговорка содержится в самом RFC 9309: это просьбы, а не enforcement.[1] Краулеры, которые важнее всего для нагрузки на сервер, это как раз те, что могут не читать или не соблюдать robots.txt. Для этого и нужны nginx-уровни.

Уровень 2: map + return 444, слой enforcement

Модуль ngx_http_map_module создаёт переменную, значение которой зависит от другой переменной, а маппинг задаётся один раз в контексте http.[8] Его regex-синтаксис это ровно то, что нужно для списка блокировки по user-agent: ~ для регистрозависимого матчинга и ~* для регистронезависимого, проверка по порядку, плюс default для всего остального.[8]

Положите это в файл, подключаемый из блока http (обычно /etc/nginx/conf.d/ai-scrapers.conf):

# /etc/nginx/conf.d/ai-scrapers.conf
# Матчим токен, а не полную строку UA: версии меняются.
map $http_user_agent $is_ai_scraper {
    default 0;

    ~*GPTBot            1;
    ~*OAI-SearchBot     1;   # уберите, если хотите, чтобы поиск ChatGPT цитировал вас
    ~*ClaudeBot         1;
    ~*Claude-SearchBot  1;
    ~*Bytespider        1;
    ~*Amazonbot         1;
    ~*Meta-ExternalAgent 1;
    ~*meta-externalagent 1;
}

Затем в server-блоке вашего сайта блокируем совпадения. Самый чистый ход в stock-nginx это return 444, нестандартный код nginx, который закрывает соединение вообще без отправки заголовков ответа.[9] Краулеру это стоит полного TCP-раундтрипа, и парсить нечего:

# в server { }
if ($is_ai_scraper) {
    return 444;
}

Две практические заметки про этот уровень.

Матчите токен, а не полную строку. Пример строки GPTBot от OpenAI заканчивается compatible; GPTBot/1.4; +https://openai.com/gptbot, и номер версии меняется.[2] Подстрока-регэксп ~*GPTBot ловит её независимо от версии. То же для Amazonbot/0.1 и meta-externalagent/1.1.[4][6] Если краулер крутит регистр UA, ~* (регистронезависимый) справится; хотите строгости, используйте ~.

Для флуда предпочитайте 444, а не 403, но логируйте решение. return 403 возвращает настоящий HTTP-ответ, то есть соединение завершается и запрос виден в access-логе как 4xx. return 444 молча рвёт соединение. Для защиты 444 дешевле; для отладки и честной аналитики, возможно, захотите 403, чтобы блоки было можно посчитать. Разумный компромисс: оставить 444 для известных трейнинг-краулеров и считать их в отдельном access-логе либо просто грепать по $http_user_agent.

Проверьте curl’ом до того, как перезагружать в прод:

curl -sS -o /dev/null -w '%{http_code}\n' -A 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; GPTBot/1.4; +https://openai.com/gptbot' https://example.com/
# 000  -> соединение закрыто без ответа (444 сработал)
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/
# 200  -> обычные браузеры не затронуты

Уровень 3: limit_req, демпфер флуда

Блокировка по user-agent убирает краулеров, которые представляются. Остаётся флуд тех, кто подделывает настоящий браузерный UA или не шлёт UA вовсе. Для этого правильный инструмент это ngx_http_limit_req_module, который реализует rate limit по модели leaky bucket, ключуемый любой переменной, обычно клиентским IP.[10]

Зона объявляется в контексте http, а ссылаются на неё в server или location:

# /etc/nginx/conf.d/bot-flood.conf (контекст http)
limit_req_zone $binary_remote_addr zone=perip:10m rate=5r/s;

# server { ... location / { ... } }
server {
    listen 443 ssl;
    server_name example.com;

    location / {
        # proxy_pass / корневой конфиг из вашего reverse-proxy...

        limit_req zone=perip burst=20 nodelay;
    }
}

Несколько фактов из документации модуля, которые определяют настройку:

  • Зона использует модель leaky bucket: запросы обрабатываются со скоростью rate, избыток задерживается до burst, а сверх этого запрос отклоняется.[10]
  • nodelay убирает очередь: лишние запросы отклоняются сразу, а не задерживаются. Используйте для бот-трафика, где задержанный ответ бессмыслен; опускайте там, где лучше притормозить, чем отрезать.[10]
  • Параметр delay (nginx 1.15.7+) задерживает до лимита и отклоняет сверх него, что даёт хорошую защиту API от всплесков без очереди флуда.[10]
  • limit_req_status по умолчанию равен 503; можно поставить 429 Too Many Requests, если это лучше ложится на контракт с клиентами.[10]
  • Переменная $limit_req_status (1.17.6+) сообщает PASSED, DELAYED или REJECTED по каждому запросу, так что можно логировать и считать, что реально сделал лимитер.[10]
  • Арифметика размера зоны: один мегабайт зоны держит примерно 16 тысяч 64-байтных состояний на 32-битных платформах или 8 тысяч 128-байтных на 64-битных, и состояние создаётся на каждый отдельный ключ (здесь, на клиентский IP).[10] Зоны 10m хватает небольшому VPS с запасом; размерьте под число ожидаемых уникальных IP, а не под частоту запросов.

Универсально правильной ставки не существует, и мы не будем её выдумывать. Честный процесс: посмотрите реальные access-логи, найдите пиковую частоту вашего нормального трафика (люди, ваш мониторинг) и поставьте лимит на порядок выше, чтобы отрезался флуд, а не пользователи. Для безопасного ввода в эксплуатацию есть limit_req_dry_run on (1.17.1+): он считает лишние запросы в зоне без применения, и это правильный способ проверить новый лимит на живом трафике до того, как он начнёт отклонять.[10]

Две оговорки про IP-лимиты. Первая: IPv6 меняет картину: $binary_remote_addr занимает 16 байт для IPv6, и /64 за NAT или большой прокси-пул могут выглядеть как один адрес, так что per-IP лимит способен отсечь легитимных пользователей на общих адресах. Вторая: распределённый флуд с многих IP обходит per-IP лимитер полностью; в этой точке лимит это последний рубеж для вашего origin, а не лекарство.

Уровень 4: TLS-фингерпринты (JA3/JA4), и почему это сложно

Краулер, который обходит уровни 2 и 3, это тот, кто шлёт настоящий Chrome-юзер-агент с многих IP. Обычный способ ловить таких это TLS-фингерпринт клиента: JA3 (MD5-хеш полей TLS ClientHello) или его преемник JA4, который нормализует тот же сигнал в читаемую строку. Разные HTTP-клиенты отдают наблюдаемо разные формы ClientHello, поэтому настоящий Chrome на Windows и Python-HTTP-клиент AI-скрейпера дают разные фингерпринты даже с одинаковыми user-agent.

Вот честная часть: в stock-nginx нет поддержки JA3/JA4. Это не директива из документации nginx, и в дистрибутивном пакете вы её не найдёте. Реализация означает сборку nginx из исходников с модулем третьей стороны и пропатченным OpenSSL, который отдаёт сырой ClientHello. Проекты вроде nginx-ssl-fingerprint делают ровно это: патчат ядро nginx и OpenSSL и выставляют переменные вроде $http_ssl_ja3 и $http_ssl_ja4, которые затем можно использовать в map точно так же, как user-agent.[13] Работает и является реальной возможностью, но это обязательство на сборку из исходников: вы берёте на себя поддержку пропатченных nginx и OpenSSL. Для большинства небольших VPS такая цена обслуживания не оправдана, а UA-мап плюс rate limiting и так убирают подавляющую часть AI-скрейперского трафика.

Реалистичная альтернатива, когда нужна защита на уровне фингерпринта без сборки, это поставить перед nginx управляемый edge, который делает фингерпринтинг за вас. Это другой деплой, а не то, что nginx делает сам по себе.

Не сломайте поисковики

Весь смысл матчинга токена, а не блокировки всего подряд, в том, что настоящие поисковые краулеры представляются так же, и вы хотите, чтобы они продолжали приходить. Краулеры Google идентифицируются тремя способами: user-agent, исходный IP и reverse-DNS-хостнейм, и Google публикует и IP-диапазоны, и процедуру проверки, чтобы отличить настоящего Googlebot от подделки.[14] Если собираетесь агрессивно раздавать return 444, вынесите Googlebot, Bingbot и YandexBot в явный allowlist и проверьте выборку их запросов по reverse DNS, прежде чем доверять лог-строке, которая называет себя одним из них.

Блокировка Googlebot общим правилом это то, как сайт тихо исчезает из Google Search. Именно поэтому Google вынесла Google-Extended отдельно: можно оставить Googlebot краулить и индексировать, при этом отказавшись от обучения Gemini, целиком через robots.txt и без всякого участия nginx.[5]

Проверьте, затем перезагрузите

Внесите изменения обычным способом и валидируйте каждый уровень независимо:

sudo nginx -t          # конфиг сначала должен чисто парситься
sudo systemctl reload nginx

# проверки против живого сервера
curl -sS -o /dev/null -w '%{http_code}\n' -A 'GPTBot/1.4' https://example.com/
curl -sS -o /dev/null -w '%{http_code}\n' -A 'Bytespider' https://example.com/
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/     # люди по-прежнему 200

# смотрим, что сделал лимитер
grep ' 503 ' /var/log/nginx/access.log | tail

Если перезагрузки автоматизированы, гайд по Control API показывает, как превратить перезагрузку в запрос, отвечающий JSON-вердиктом вместо строки в логе.

Короткая версия

Три уровня stock-nginx закрывают практический случай 2026 года: robots.txt по RFC 9309 для сотрудничающих краулеров (блокируем GPTBot, ClaudeBot, Bytespider, Amazonbot, Meta-ExternalAgent и токен Google-Extended; оставляем OAI-SearchBot, если хотите, чтобы ChatGPT вас цитировал); map по $http_user_agent с ~*-регэкспами токенов и return 444 для краулеров, которые представляются; и зона limit_req по ключу $binary_remote_addr с nodelay для флуда, который подделывает или пропускает user-agent. Сначала валидируйте через limit_req_dry_run, и никогда не бейте Googlebot, Bingbot и YandexBot той же кистью. Фингерпринтинг JA3/JA4 реален, но требует nginx из исходников с модулем третьей стороны, так что относитесь к нему как к пути эскалации, а не как к умолчанию. Дефолты выше это те, что реально ставятся на stock-сервер.

Источники

  1. RFC 9309 - Robots Exclusion Protocol (группы, product-токены, longest-match; «не форма авторизации доступа»; /robots.txt разрешён всегда). https://www.rfc-editor.org/rfc/rfc9309.html - проверено 2026-10-13
  2. OpenAI - Overview of OpenAI Crawlers (GPTBot, OAI-SearchBot, ChatGPT-User; теги robots.txt; опубликованные IP; обновление ~24ч). https://developers.openai.com/api/docs/bots - проверено 2026-10-13
  3. Anthropic - Does Anthropic crawl data from the web… (ClaudeBot, Claude-SearchBot, Claude-User; поддержка Crawl-delay; соблюдение robots.txt). https://support.anthropic.com/en/articles/8896518-does-anthropic-crawl-data-from-the-web-how-can-site-owners-block-the-crawler - проверено 2026-10-13
  4. Amazon - About AmazonBot (Amazonbot/0.1; соблюдает Robots Exclusion Protocol; не поддерживает crawl-delay; robots-мета-теги; опубликованные IP). https://developer.amazon.com/amazonbot - проверено 2026-10-13
  5. Google - Google’s common crawlers (Google-Extended это отдельный product-токен для обучения/grounding в Gemini; без влияния на Google Search). https://developers.google.com/crawling/docs/crawlers-fetchers/google-common-crawlers - проверено 2026-10-13
  6. Meta - Meta Web Crawlers (Meta-ExternalAgent обучает foundation-модели AI / индексирует контент). https://developers.facebook.com/docs/sharing/webmasters/crawler - проверено 2026-10-13
  7. monperrus/crawler-user-agents - поддерживаемый список user-agent краулеров (Bytespider, GPTBot, Amazonbot, meta-externalagent, OAI-SearchBot, Claude-SearchBot). https://github.com/monperrus/crawler-user-agents - проверено 2026-10-13
  8. nginx - Module ngx_http_map_module (map-переменные, регэкспы ~/~*, default). https://nginx.org/en/docs/http/ngx_http_map_module.html - проверено 2026-10-13
  9. nginx - Module ngx_http_rewrite_module (return 444 закрывает соединение без ответа). https://nginx.org/en/docs/http/ngx_http_rewrite_module.html - проверено 2026-10-13
  10. nginx - Module ngx_http_limit_req_module (leaky bucket, burst, nodelay, delay 1.15.7+, dry_run 1.17.1+, limit_req_status по умолчанию 503, $limit_req_status 1.17.6+, арифметика размера зоны). https://nginx.org/en/docs/http/ngx_http_limit_req_module.html - проверено 2026-10-13
  11. nginx - download (mainline 1.31.8, 2026-10-21; stable 1.30.6, 2026-10-14). https://nginx.org/en/download.html - проверено 2026-11-07
  12. Cloudflare Radar - Bot traffic from AS13238 (Yandex), последние 7 дней (классификация: 99,6% бот); тред Hacker News «Sudden drop in Cloudflare bot traffic after strike on Yandex datacenter» (сообщество сообщает, 2026-10-11). https://radar.cloudflare.com/bots/as13238 ; https://news.ycombinator.com/item?id=50041619 - проверено 2026-10-13
  13. nginx-ssl-fingerprint - модуль JA3/JA4 для nginx (требует патчей ядра nginx и OpenSSL, сборка из исходников; отдаёт $http_ssl_ja3/$http_ssl_ja4). https://github.com/phuslu/nginx-ssl-fingerprint - проверено 2026-10-13
  14. Google - Overview of Google crawlers and fetchers (идентификация по user-agent, исходному IP и reverse DNS). https://developers.google.com/crawling/docs/crawlers-fetchers/overview-google-crawlers - проверено 2026-10-13