Сайт открывается в браузере, отдаёт 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 re import subprocess import sys USER_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, body def 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: можно прислать ссылку и получить готовый отчёт без терминала.

