Сайт открывается в браузере, отдаёт 200 OK, страница рендерится без единой ошибки. При этом ни ChatGPT, ни Claude, ни Perplexity ни разу не сослались на ваш контент. Первое, на что обычно грешат в этой ситуации, — качество текста или его SEO-разметку. На практике причина чаще лежит на уровень ниже: боты этих систем физически не получают ту страницу, которую видите вы.
Особенно часто это касается проектов на shared-хостинге или за CDN. Антибот-защита там нередко включена по умолчанию — провайдером или хостинг-панелью, а не осознанным решением владельца сайта. Cloudflare, WAF хостинга или модуль защиты от парсинга можно годами не замечать: обычным посетителям и поисковым ботам Google он не мешает, а вот новые краулеры вроде GPTBot или ClaudeBot под раздачу попадают в первую очередь — их либо ещё не занесли в белый список, либо режут по репутации IP-подсети или по самому User-Agent. Итог один: администратор сайта искренне не подозревает, что его контент невидим для ИИ-систем, потому что в браузере и в Google Search Console всё выглядит идеально.
Проверить это на глаз нельзя — нужно посмотреть на сайт глазами конкретного бота. Ниже — рабочий способ сделать это одной командой терминала, список ботов, которых стоит проверять поимённо, и Python-скрипт, автоматизирующий всю проверку целиком.
Ловушка HEAD-запроса
Первое, на чём спотыкается почти любая ручная проверка: curl -I вашего домена.
curl -I https://example.com
Сервер честно отвечает HTTP/2 200. Логичный вывод — «всё открыто». Вывод неверный. -I отправляет HEAD-запрос: сервер возвращает заголовки и не выполняет тело обработки страницы. А фильтрующий скрипт — Cloudflare, WAF, антибот-модуль — чаще всего висит именно на GET, там, где отдаётся тело ответа.
Проверка ничего не стоит, если она сама себя обманывает. Правильный тест — полноценный GET с выводом заголовков и телом:
curl -sS -L -A "Mozilla/5.0 (compatible; GPTBot/1.0; +https://openai.com/gptbot)" \
-D - -o /tmp/response.html https://example.com
Здесь:
-
-A — подмена User-Agent (представляемся конкретным ботом);
-
-L — следование редиректам, иначе легко принять 301 на страницу проверки за целевую страницу;
-
-D — — заголовки ответа в stdout;
-
-sS — тихий режим, но с показом ошибок;
-
-o — тело ответа сохраняется отдельно, чтобы его можно было прогрепать.
Кого проверять поимённо
У каждой ИИ-системы свой краулер, и они ведут себя по-разному — часть сайтов блокирует одних ботов и пропускает других. Проверять «в целом доступность сайта» бессмысленно: нужно пройтись по каждому.
|
Бот |
Кто использует |
|---|---|
|
GPTBot |
обучение моделей OpenAI |
|
ChatGPT-User |
live-запросы ChatGPT по ссылке пользователя |
|
ClaudeBot |
обучение моделей Anthropic |
|
Claude-SearchBot |
поиск и цитирование Claude |
|
PerplexityBot |
индексация Perplexity |
|
Google-Extended |
использование контента в Gemini / AI Overviews |
|
Applebot-Extended |
Apple Intelligence |
|
CCBot |
Common Crawl — датасет, на котором учится половина моделей |
|
Amazonbot |
ассистенты Amazon |
|
bingbot |
Bing + Copilot |
|
meta-externalagent |
Meta AI |
Если сайт закрыл CCBot два года назад «на всякий случай» и забыл — вот и объяснение, почему модели о вас не знают.
Что означает каждый ответ
После прогона GET-запроса с нужным User-Agent результат укладывается в три сценария:
-
200 и чистый HTML. Тело ответа содержит реальный контент страницы. Бот видит то же, что и человек. Это цель.
-
200, но с проверкой. Статус успешный, а в теле — форма капчи или JS-челлендж Cloudflare. Формально сайт «доступен», фактически бот получает пустышку. Проверяется так:
grep -Ei 'captcha|challenge|cloudflare|verify you are human' /tmp/response.html
Совпадение — сигнал, что WAF отфильтровал именно этого клиента.
-
403 или 503. Прямой запрет. Часто — правило файрвола по User-Agent или по подсети, из которой ходит краулер.
Каждый сценарий требует разного исправления: правило в robots.txt не решает проблему №2, а настройка Cloudflare bot-fight mode не решает проблему №3.
Скрипт: автоматизация curl для всех ботов сразу
Ручной прогон одиннадцати команд ради одной проверки не масштабируется. Ниже — скрипт, который оборачивает curl в цикл по всем User-Agent, парсит финальный статус после редиректов и классифицирует ответ.
#!/usr/bin/env python3"""Проверка доступности сайта для AI-ботов: curl с разными User-Agent."""import reimport subprocessimport sysUSER_AGENTS = { "GPTBot": "Mozilla/5.0 (compatible; GPTBot/1.0; +https://openai.com/gptbot)", "ChatGPT-User": "Mozilla/5.0 (compatible; ChatGPT-User/1.0; +https://openai.com/bot)", "ClaudeBot": "Mozilla/5.0 (compatible; ClaudeBot/1.0; +claudebot@anthropic.com)", "Claude-SearchBot": "Mozilla/5.0 (compatible; Claude-SearchBot/1.0; +claudebot@anthropic.com)", "PerplexityBot": "Mozilla/5.0 (compatible; PerplexityBot/1.0; +https://perplexity.ai/perplexitybot)", "Google-Extended": "Mozilla/5.0 (compatible; Google-Extended/1.0)", "Applebot-Extended": "Mozilla/5.0 (compatible; Applebot-Extended/1.0)", "CCBot": "CCBot/2.0 (+https://commoncrawl.org/faq/)", "Amazonbot": "Mozilla/5.0 (compatible; Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)", "bingbot": "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)", "meta-externalagent": "meta-externalagent/1.1",}CHALLENGE_MARKERS = re.compile(r"captcha|challenge|cloudflare|verify you are human", re.I)def fetch(url: str, user_agent: str) -> tuple[int, str]: """Выполняет полноценный GET через curl и возвращает (финальный статус, тело).""" result = subprocess.run( ["curl", "-sS", "-L", "-A", user_agent, "-D", "-", "-o", "-", "--max-time", "15", url], capture_output=True, text=True, check=False, ) # -L может вернуть несколько блоков заголовков (по одному на редирект), # разделённых пустой строкой — тело всегда в последнем куске. parts = result.stdout.split("\r\n\r\n") body = parts[-1] headers = "\r\n\r\n".join(parts[:-1]) statuses = re.findall(r"HTTP/\S+\s+(\d+)", headers) status = int(statuses[-1]) if statuses else 0 return status, bodydef classify(status: int, body: str) -> str: if status in (403, 503): return "ЗАБЛОКИРОВАН" if status == 200 and CHALLENGE_MARKERS.search(body): return "ПРОВЕРКА (WAF/Cloudflare)" if status == 200: return "ДОСТУПЕН" return f"НЕИЗВЕСТНО ({status})"def run_report(url: str) -> None: print(f"{'Бот':<20} {'HTTP':<6} Результат") print("-" * 50) for name, ua in USER_AGENTS.items(): status, body = fetch(url, ua) print(f"{name:<20} {status:<6} {classify(status, body)}")def _demo() -> None: """Самопроверка логики классификации без сетевых запросов.""" assert classify(403, "") == "ЗАБЛОКИРОВАН" assert classify(200, "please verify you are human") == "ПРОВЕРКА (WAF/Cloudflare)" assert classify(200, "<html>ok</html>") == "ДОСТУПЕН" assert classify(500, "") == "НЕИЗВЕСТНО (500)" print("self-check OK\n")if __name__ == "__main__": _demo() if len(sys.argv) > 1: run_report(sys.argv[1]) else: print("Использование: python check_ai_bots.py https://example.com")
Запуск:
python3 check_ai_bots.py https://example.com
Вывод — таблица из одиннадцати строк: бот, HTTP-статус, вердикт. Ничего лишнего — то, что нужно, чтобы сразу понять, для кого конкретно сайт закрыт.
Скрипт зависит только от установленного curl и стандартной библиотеки Python — ничего ставить не нужно.
Что делать с результатом
-
Статус 403/503 у конкретного бота — ищите правило файрвола или CDN по имени этого User-Agent и снимайте его точечно, не отключая защиту целиком.
-
200 с челленджем — исключите User-Agent’ы ботов из правил bot-fight/JS-challenge в настройках Cloudflare или WAF.
-
200 и чисто — сайт для этого бота открыт; если цитирований всё равно нет, причина уже не в доступности, а в структуре и качестве контента.
Если не хочется собирать это руками — весь процесс проверки доступности сайта для ИИ-ботов уже реализован в телеграм-боте @AvigroupAI_bot: можно прислать ссылку и получить готовый отчёт без терминала.
ссылка на оригинал статьи https://habr.com/ru/articles/1074550/