Настоящий краулер или подделка
Что решаем
Заголовок раздела «Что решаем»В User-Agent запроса написано GPTBot. User-Agent — это свободный заголовок, в котором клиент сам себя называет, и вписать туда можно что угодно. Мой лог nginx не отличал настоящий краулер от сканера под его именем, и я считал их вместе.
Поэтому я снял тридцать дней из Cloudflare, который стоит перед всеми сайтами моего домена. По зоне целиком неподтверждённых имён краулеров оказалось почти в пять раз больше, чем подтверждённых. На одном сайте, voice.smolevich.com, картина вышла обратная.
1. Почему имени в заголовке нельзя верить
Заголовок раздела «1. Почему имени в заголовке нельзя верить»User-Agent — это то, что клиент решил отправить, так что curl -A "GPTBot/1.1" https://example.com/ кладёт в ваш лог ровно ту же строку, что и настоящий краулер, и по самой строке их не различить.
За 30 дней, с 17 сентября по 16 октября 2026, моя зона приняла 245 883 запроса. 8 249 из них пришли под именами GPTBot, ClaudeBot, PerplexityBot, OAI-SearchBot или ChatGPT-User без подтверждения Cloudflare. Подтверждённых под этими пятью именами было 1 689.
Что это за запросы, видно по тому, куда они шли. Запросов с .env в пути — это файл, где приложения держат пароли и ключи, — по зоне набралось 19 398. Из 1 996 неподтверждённых запросов PerplexityBot только 60 попали на страницы, которые у меня есть.
2. Как Cloudflare решает, что бот настоящий
Заголовок раздела «2. Как Cloudflare решает, что бот настоящий»У Cloudflare есть список verified bots — подтверждённых ботов. Владелец бота регистрирует шаблон User-Agent и способ доказать, откуда идут его запросы.
Способов три: опубликованный список диапазонов IP, который Cloudflare забирает сам, обратный DNS — IP резолвится обратно в домен, названный владельцем, например googlebot.com, — или криптографическая подпись на каждом запросе, она называется Web Bot Auth.
Запрос, прошедший проверку, получает категорию: Search Engine Crawler, AI Crawler, AI Assistant, AI Search и ещё с десяток. Запрос, который проверку не прошёл или пришёл от незарегистрированного бота, получает пустую категорию, и это пустое поле — весь мой тест.
Один бот не проходит его нарочно: в августе 2025 Cloudflare убрал Perplexity из подтверждённых ботов, после того как поймал его на обходе сайтов под необъявленными именами. Так что любой запрос PerplexityBot неподтверждённый, настоящий тоже.
3. Что видно на бесплатном тарифе, а что нет
Заголовок раздела «3. Что видно на бесплатном тарифе, а что нет»На бесплатном тарифе есть категория подтверждённого бота, и ничего тоньше.
| Что | Free | Платно |
|---|---|---|
| Категория подтверждённого бота — в правилах, GraphQL и дашборде | да | да |
cf.client.bot, true для подтверждённого бота | да | да |
| Группы вроде «likely automated» | нет | от Pro |
| Bot score, оценка 1–99 на каждый запрос | нет | Enterprise Bot Management |
| Regex в правилах | нет | от Business |
| ASN как поле GraphQL | нет | не проверял |
ASN — номер сети, которой принадлежит IP, например облачного провайдера, — стоил мне вечера. GraphQL моей зоны ответил does not have access to the field 'clientasn'. В дашборде ASN всё равно печатается у каждого запроса из выборки, по одной строке.
На бесплатном тарифе есть и Bot Fight Mode, который показывает челлендж простым ботам с облачных хостингов и headless-браузерам, но не говорит, кого поймал.
4. Разделить в дашборде
Заголовок раздела «4. Разделить в дашборде»- Откройте зону, затем Security → Analytics, вкладка Traffic.
- Нажмите Filter и наберите
bot. На моей бесплатной зоне из ботовых полей было только Verified Bot Category. - Выберите категорию, например
AI Crawler, и смотрите график: это подтверждённый трафик. - Пролистайте до Sampled logs и раскройте строку. У подтверждённого запроса рядом с IP, страной, ASN и User-Agent стоит Verified Bot Category: AI Crawler. У подделки имя бота есть в User-Agent, а строки с категорией нет.
Фильтра по User-Agent в бесплатном дашборде нет, так что спросить его «всё, что называет себя GPTBot и не подтверждено» нельзя — для этого нужен GraphQL.
5. Разделить через GraphQL
Заголовок раздела «5. Разделить через GraphQL»GraphQL Analytics — API запросов Cloudflare по адресу https://api.cloudflare.com/client/v4/graphql. Нужен API-токен с правом Zone → Analytics → Read.
Один день на запрос держит вас ниже лимита строк. Adaptive-данные — выборочный датасет по запросам, который читает этот запрос, — хранятся не меньше 31 дня на любом тарифе, поэтому тридцать дней — самое длинное окно, которое я смог взять.
curl -s https://api.cloudflare.com/client/v4/graphql \ -H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: application/json" \ --data @- <<'EOF'{"query": "{ viewer { zones(filter:{zoneTag:\"YOUR_ZONE_ID\"}) { rows: httpRequestsAdaptiveGroups(limit:10000, filter:{datetime_geq:\"2026-10-17T00:00:00Z\", datetime_lt:\"2026-10-18T00:00:00Z\", clientRequestHTTPHost:\"example.com\"}, orderBy:[count_DESC]) { count dimensions { userAgent verifiedBotCategory clientRequestPath clientCountryName edgeResponseStatus } } } } }"}EOFДальше каждую строку разбирайте по двум вопросам: есть ли в userAgent имя бота и пустая ли строка в verifiedBotCategory. Имя есть, а категория пустая — такой запрос я называю неподтверждённым.
jq -r '.data.viewer.zones[0].rows[] | select(.dimensions.userAgent | test("gptbot|claudebot|perplexitybot|oai-searchbot|chatgpt-user|googlebot|bingbot"; "i")) | [(if .dimensions.verifiedBotCategory == "" then "unconfirmed" else "verified" end), .count, .dimensions.clientRequestPath] | @tsv' day.json | sort | head -40count здесь оценка. Adaptive-группы выборочные, и итог по моей зоне вышел на 1.6% выше собственного суточного счётчика Cloudflare.
6. Одно правило, которое режет подделки
Заголовок раздела «6. Одно правило, которое режет подделки»WAF custom rule — фильтр, который Cloudflare прогоняет на каждом запросе до вашего сервера, — состоит из выражения и действия. На бесплатном тарифе таких правил пять.
Откройте Security → Security rules, создайте custom rule, переключитесь на Edit expression и вставьте:
(lower(http.user_agent) contains "gptbot" or lower(http.user_agent) contains "claudebot" or lower(http.user_agent) contains "oai-searchbot" or lower(http.user_agent) contains "chatgpt-user" or lower(http.user_agent) contains "googlebot" or lower(http.user_agent) contains "bingbot")and not cf.client.botДействие — Block. lower() и contains заменяют regex, которого на бесплатном тарифе нет, а not cf.client.bot значит «Cloudflare этот запрос не подтвердил». Та же проверка через поле категории — cf.verified_bot_category eq "".
PerplexityBot я в правило не включил нарочно. Под этим именем не подтверждено ничего, и правило заблокировало бы настоящий краулер вместе с подделками. На моём voice-сайте это 141 запрос, и выглядели они настоящими.
7. Цифры по voice.smolevich.com
Заголовок раздела «7. Цифры по voice.smolevich.com»Свежая выгрузка, с 18 сентября по 17 октября 2026, только хост voice.smolevich.com: 45 219 запросов.
| Имя бота в User-Agent | Подтверждён | Без подтверждения |
|---|---|---|
| Googlebot | 673 | 7 |
| Bingbot | 568 | 0 |
| ChatGPT-User | 498 | 0 |
| OAI-SearchBot | 283 | 0 |
| ClaudeBot | 121 | 1 |
| GPTBot | 106 | 0 |
| PerplexityBot | 0 | 141 |
| Итого | 2 249 | 149 |
Куда шли эти 149:
| Куда | Запросов |
|---|---|
| Настоящие страницы из sitemap | 83 |
/robots.txt | 45 |
| Картинки и аудиопримеры с этих страниц | 13 |
| Собственный скрипт Cloudflare для защиты почты | 8 |
/.env, wp-* и прочие зонды | 0 |
148 из 149 пришли из США и один из Сербии. Тот один запросил главную как Googlebot, а в Сербии работаю я сам, так что считаю его, скорее всего, своим.
Сканеры на этом сайте именем бота не прикрываются. На пути с .env пришло 39 запросов и на wp-* ещё 3, и ни один не назвался ботом. У всех запросов к .env была обычная строка браузера.
В общем, на этом сайте правило выше заблокировало бы 8 запросов за месяц. Шесть из них — запрос скрипта Cloudflare под именем Googlebot, и это вполне может быть собственный рендерер Google. По всей зоне то же правило бьёт по тысячам.
8. Случай: настоящий Claude тоже попал под правило
Заголовок раздела «8. Случай: настоящий Claude тоже попал под правило»Правило — как охранник на входе. Кто говорит «я бот», того охранник сверяет со своим списком настоящих ботов. Claude, когда открывает страницу по просьбе человека в чате claude.ai, в этом списке не значился, и охранник не пустил и его. Мы дописали охраннику одну строку: Claude с адресов самой Anthropic пропускать.
Правило зоны smolevich.com отправляет запрос на Managed Challenge, если в User-Agent имя ИИ-бота и выполняется not cf.client.bot. Claude-User — это агент, которым claude.ai открывает страницу по просьбе пользователя. Он приходит с 160.79.106.x, и проверенным ботом Cloudflare его не считает. 22 и 23 октября 2026 правило зачелленджило его 10 раз.
Исправили 23 октября. Одно условие:
and not (http.user_agent contains "Claude-User" and ip.src in {160.79.104.0/21})160.79.104.0/21 — ровно тот диапазон, который Anthropic публикует как свои исходящие адреса, а ARIN записывает блок на Anthropic, PBC. Список краулеров Anthropic bots.json тут не помогает: в нём 216.73.216.0/22 и отдельные адреса, 160.79.x там нет.
Почему подсеть, а не ASN. По RIPEstat этот /21 анонсируют три сети: 160.79.106.0/24 — AS396982 (Google Cloud), 160.79.104.0/23 — AS399358 (Anthropic), 160.79.108.0/22 — AS16509 (Amazon). Claude-User ходит из части Google Cloud, но с того же AS396982 идут и подделки, так что исключение по ASN пропустило бы их вместе с настоящим Claude.
Подделку видно и по подписи в конце User-Agent, если присмотреться. Настоящий заканчивает User-Agent на [email protected]. Поддельный из Google Cloud писал [email protected].
Неделя 17–23 октября, по выборке событий файрвола:
| Что остановило правило | Запросов |
|---|---|
| Все срабатывания | 1 042 |
Поддельные боты из Google Cloud, искали /.env, /.env.local, /.gitlab-ci.yml | 911 |
| Настоящий Claude-User, все до правки | 10 |
После правки Claude-User с этих адресов получал ответы 200.
Что не сработало
Заголовок раздела «Что не сработало»- API-токен Cloudflare, который у меня уже был. Он выпущен под аналитику и ответил
Authentication error: дата начала действия у него стоит в 2027 году. Тот же GraphQL я выполнил из вкладки дашборда, где уже был вход, через его собственный/api/v4/graphql. - Топ ASN у подделок. На моей бесплатной зоне GraphQL отказал в
clientAsn, поэтому даю страны. ASN есть только в дашборде, построчно. - Проверка правила через API до публикации. Старый эндпоинт
filters/validate-exprответил 404. Синтаксис я взял из примеров документации и правило не разворачивал, так что на бесплатной зоне оно пока не проверено. - Перенос цифр по зоне на каждый сайт. История зоны — подделки повсюду и охота за
.env— на voice.smolevich.com не подтвердилась. Там почти все неподтверждённые запросы оказались PerplexityBot на настоящих страницах. - Считать
.pngи.mp3зондами. Мой первый фильтр путей ловил всё с расширением файла. Тринадцать «зондов» оказались картинками и аудио с моих же страниц.
Проверить
Заголовок раздела «Проверить»- Выполните запрос GraphQL выше за вчера по своему хосту и посчитайте неподтверждённые строки. Потом посмотрите, на какие пути они шли, и сравните со своим sitemap.
- Отправьте подделку сами:
curl -A "GPTBot/1.1" https://your-site/. В следующей выгрузке она должна появиться с пустойverifiedBotCategory. - После того как развернёте правило, повторите тот же
curl. Cloudflare должен ответить 403, а блок появится в Security → Analytics → Events. - Проверьте, что подтверждённые боты проходят. Их категории по-прежнему видны в фильтре вкладки Traffic.
- Строки PerplexityBot прочитайте отдельно, прежде чем решать, блокировать ли его. Мои выглядели как настоящий краулер.
Настройки доступа и кому принадлежит какой агент: AI-crawler и llms.txt. Тот же вопрос к логу nginx: кто к вам на самом деле ходит.
