Когда боты ChatGPT приходят на сайт с зарубежных адресов, они делают много запросов подряд и ничего не рендерят – для защиты от ботов это поведение скрапера. Боты получают 403, страница не попадает в источники ответов, а в логах всё выглядит как отбитая атака, и никто ничего не замечает.
Лечится это белым списком: адреса, с которых ходят настоящие боты, исключаются из общих правил. Мешают две вещи. Первая – список живой: вендоры добавляют и убирают подсети без анонсов, изредка сразу тысячами адресов. В августе Apple расширила пул Applebot на несколько тысяч адресов, и все статические списки, написанные до этого, разом перестали быть полными. Вторая – заметная доля заходов с User-Agent ИИ-бота сделана вовсе не ботом: имя подделывается одной строкой.
Дальше – как собрать белый список скриптом из официальных источников, как отличить настоящего бота от самозванца по обратной зоне, как превратить всё это в конфиги nginx и iptables и как проверить по логам, что оно работает.
Зачем сайту белый список адресов
Задача выглядит просто: не мешать ИИ-ботам читать сайт. На практике мешают три вещи:
1. Защита от ботов. Cloudflare, антифрод-прослойки и обычный `fail2ban` режут запросы по поведению, а не по имени: правило срабатывает раньше, чем кто-нибудь смотрит на User-Agent. Отличить обход ChatGPT от парсера-скрапера по одному только поведению нельзя – много запросов, нет рендеринга, нет мыши и там, и там.
2. Rate limiting. Лимит в 30 запросов с адреса в минуту звучит безобидно, пока обход не приходит с одной подсети и не упирается в него на десятой странице.
3. Геофильтры. Часть проектов режет трафик из-за рубежа целиком. Все перечисленные боты ходят с зарубежных адресов.
Отсюда потребность в белом списке – перечне доверенных диапазонов, которые исключаются из общих правил (в документации вендоров он называется allowlist).
Где лежат официальные фиды
Четыре вендора публикуют диапазоны так, чтобы ИИ спокойно их читал, в одном и том же формате – JSON со списком префиксов:
|
Бот |
Фид |
Назначение |
|
Applebot |
`search.developer.apple.com/applebot.json` |
Siri, Spotlight, обучение моделей Apple |
|
GPTBot |
`openai.com/gptbot.json` |
обход для обучения |
|
OAI-SearchBot |
`openai.com/searchbot.json` |
обход для поисковой части ChatGPT |
|
ChatGPT-User |
`openai.com/chatgpt-user.json` |
заход по действию пользователя |
|
PerplexityBot |
`perplexity.ai/perplexitybot.json` |
обход Perplexity |
Формат одинаковый, поэтому парсер пишется один раз:
import json, urllib.request, ipaddress
UA = {"User-Agent": "allowlist-builder/1.0"}
FEEDS = {
"Applebot": "https://search.developer.apple.com/applebot.json",
"GPTBot": "https://openai.com/gptbot.json",
"OAI-SearchBot": "https://openai.com/searchbot.json",
"ChatGPT-User": "https://openai.com/chatgpt-user.json",
"PerplexityBot": "https://www.perplexity.ai/perplexitybot.json",
}
def fetch(url):
req = urllib.request.Request(url, headers=UA)
data = json.load(urllib.request.urlopen(req, timeout=25))
out = []
for p in data.get("prefixes", []):
cidr = p.get("ipv4Prefix") or p.get("ipv6Prefix")
if cidr:
out.append(cidr)
return out
allow = {name: fetch(url) for name, url in FEEDS.items()}
for name, cidrs in allow.items():
ips = sum(ipaddress.ip_network(c).num_addresses for c in cidrs if ":" not in c)
print(f"{name:15} {len(cidrs):4d} префиксов, {ips:7d} адресов IPv4")
Вывод на 20 августа 2026:
Applebot 33 префиксов, 7056 адресов IPv4 GPTBot 21 префиксов, 3472 адресов IPv4 OAI-SearchBot 35 префиксов, 2496 адресов IPv4 ChatGPT-User 204 префиксов, 36016 адресов IPv4 PerplexityBot 8 префиксов, 18 адресов IPv4
Что видно из этих чисел
![]()
Размер пула говорит о назначении бота больше, чем его документация.
Разброс у меня вышел на целых три порядка, и он объясняется саим устройством ботов.
PerplexityBot – 18 адресов. Восемь префиксов, почти все на /29 и /30. Это плановый обход с небольшой фермы: предсказуемый, легко белится, легко ловится по IP.
ChatGPT-User – 36 016 адресов, 204 префикса. Это не обход, а заход по действию человека: пользователь задал вопрос, система пошла за конкретной страницей прямо сейчас. Такие запросы идут из инфраструктуры, обслуживающей живых людей, отсюда объём.
Из этого следует практическое правило: ChatGPT-User блокировать нельзя ни при каких настройках. За ним стоит человек, который прямо сейчас читает ответ про вас. GPTBot закрыть можно осознанно, если не хотите отдавать контент в обучение, – на попадание в ответы это влияет косвенно. А вот закрытие OAI-SearchBot и ChatGPT-User убирает вас из ответов напрямую.
Applebot – 7 056 адресов. После августовского расширения это второй по величине пул. Apple использует его и для Siri со Spotlight, и для обучения своих моделей.
Одного списка IP недостаточно
IP-адрес подделывается на уровне заголовка: любой скрапер может представиться `GPTBot` в User-Agent. Поэтому белый список по IP решает только половину задачи, а вторую половину решает обратная зона.
Проверка выглядит так: берём адрес, получаем PTR-запись, проверяем, что домен принадлежит вендору, и делаем прямой запрос обратно – совпал ли адрес.
#!/usr/bin/env bash
# Проверка подлинности бота: forward-confirmed reverse DNS
ip="$1"
host_name=$(dig +short -x "$ip" | sed 's/\.$//')
[ -z "$host_name" ] && { echo "$ip: PTR отсутствует – подделка"; exit 1; }
case "$host_name" in
*.applebot.apple.com|*.crawl.apple.com) vendor="Apple" ;;
*.openai.com) vendor="OpenAI" ;;
*.perplexity.ai|*.perplexity.com) vendor="Perplexity" ;;
*) echo "$ip: $host_name – чужой домен"; exit 1 ;;
esac
back=$(dig +short "$host_name" | head -1)
if [ "$back" = "$ip" ]; then
echo "$ip: подтверждён ($vendor, $host_name)"
else
echo "$ip: PTR ведёт на $host_name, но обратный адрес $back – подделка"
exit 1
fi
Связка из двух проверок закрывает обе истории: фид отвечает на вопрос «этот адрес вообще принадлежит вендору», обратная зона – «этот запрос действительно оттуда».
Как превратить список в конфиг nginx и правила ipset
Дальше список превращается в то, что применяется на сервере. Для nginx это карта доверенных адресов:
def nginx_geo(allow, path="ai-bots.conf"):
lines = ["# Сгенерировано автоматически, правки будут перезаписаны",
"geo $ai_bot {", " default 0;"]
for name, cidrs in allow.items():
lines.append(f" # {name}")
lines += [f" {c} 1;" for c in cidrs]
lines.append("}")
open(path, "w").write("\n".join(lines) + "\n")
def ipset_rules(allow, path="ai-bots.ipset"):
lines = ["create ai_bots hash:net family inet -exist"]
for cidrs in allow.values():
lines += [f"add ai_bots {c} -exist" for c in cidrs if ":" not in c]
open(path, "w").write("\n".join(lines) + "\n")
В конфиге nginx переменная используется прямо в лимитах:
include /etc/nginx/conf.d/ai-bots.conf;
map $ai_bot $limit_key {
1 ""; # доверенный бот – вне лимита
default $binary_remote_addr;
}
limit_req_zone $limit_key zone=common:10m rate=30r/m;
Пустой ключ выводит запрос из-под ограничения, не требуя отдельного `location` и дублирования правил.
Как это держать в актуальном состоянии
Не перезаписывать конфиг вслепую. Если фид недоступен или вернул пустой список, скрипт должен оставить прежний файл и написать в лог. Иначе одна сетевая ошибка выносит из белого списка всех ботов сразу.
Сравнивать с последним замером. Разница списков – самая полезная строка в логе: видно, что вендор добавил подсеть, и видно, когда именно. Именно так расширение Applebot заметно без чтения новостей.
Проверять применение. После генерации – `nginx -t` и перезагрузка конфигурации, а не рестарт: перезагрузка не рвёт открытые соединения.
import subprocess, json, pathlib
snapshot = pathlib.Path("allowlist.json")
prev = json.loads(snapshot.read_text()) if snapshot.exists() else {}
if all(len(c) for c in allow.values()): # ни один фид не пустой
for name in allow:
added = set(allow[name]) - set(prev.get(name, []))
removed = set(prev.get(name, [])) - set(allow[name])
if added or removed:
print(f"{name}: +{len(added)} −{len(removed)}")
snapshot.write_text(json.dumps(allow, indent=2))
nginx_geo(allow, "/etc/nginx/conf.d/ai-bots.conf")
subprocess.run(["nginx", "-t"], check=True)
subprocess.run(["nginx", "-s", "reload"], check=True)
else:
print("пустой фид – конфиг не тронут")
Applebot и Applebot-Extended: разные вещи в одном robots.txt
Отдельная путаница, на которую стоит потратить абзац. У Apple два токена в `robots.txt`, и они управляют разными вещами.
`Applebot` – сам обход. Закрыв его, вы выпадаете из Siri и Spotlight.
`Applebot-Extended` – не отдельный бот, а сигнал о том, можно ли использовать уже собранные данные для обучения моделей Apple. Он не влияет на обход и на попадание в поисковые продукты: страницы продолжают собираться, просто не идут в обучение.
User-agent: Applebot Allow: / User-agent: Applebot-Extended Disallow: /
Такая пара означает: индексируйте для поиска и Siri, но не учите на нас модели. Обратная комбинация смысла не имеет – закрыв `Applebot`, вы автоматически лишаете смысла второй токен.
Похожее деление есть у OpenAI: `GPTBot` отвечает за обучение, `OAI-SearchBot` и `ChatGPT-User` – за поисковую часть и заходы пользователей. Решение «закрыть ИИ-ботов» одной строкой почти всегда бьёт не туда, куда планировалось.
Как проверить, что белый список действительно работает
Собранный конфиг ничего не гарантирует, пока вы не увидели подтверждённые заходы в логах. Проверка занимает одну команду и одну ночь ожидания.
# Кто из ИИ-ботов приходил за сутки и сколько запросов сделал
grep -aiE "GPTBot|OAI-SearchBot|ChatGPT-User|PerplexityBot|Applebot|ClaudeBot" \
/var/log/nginx/access.log \
| awk '{print $1}' | sort | uniq -c | sort -rn | head -20
Первая колонка – число запросов, вторая – адрес. Дальше каждый адрес из верхушки прогоняется через проверку обратной зоны из предыдущего раздела: так отделяются настоящие боты от тех, кто просто подставил себе чужой User-Agent. По моему опыту подделок в этом списке заметная доля, и без проверки статистика по ИИ-ботам получается завышенной.
Отдельно стоит посмотреть коды ответов: если подтверждённый бот стабильно получает 403 или 429, значит правило где-то выше по стеку – в облачной защите или на балансировщике, и локальный конфиг nginx до него не доходит.
# Коды ответов для подтверждённого адреса бота
grep -a "203.0.113.10" /var/log/nginx/access.log \
| awk '{print $9}' | sort | uniq -c | sort -rn
Если в выдаче преобладают двухсотые – белый список работает. Если трёх- и четырёхсотые, дальше нужно смотреть, кто именно отвечает раньше nginx.
Чего этот подход не решает
Фиды публикуют не все. У Anthropic машиночитаемого списка диапазонов для `ClaudeBot` я не нашёл – остаётся проверка по обратной зоне и по User-Agent. То же с российскими системами: у Яндекса своя схема проверки роботов, у GigaChat публичного фида нет.
Поэтому белый список по IP – не полное решение, а первый слой. Второй слой – проверка обратной зоны, третий – здравые лимиты, при которых легальный обход не упирается в потолок на десятой странице.
И последнее: белый список не делает вас видимым в ответах. Он только убирает препятствие. Если после его внедрения в логах по-прежнему нет ни одного подтверждённого захода ИИ-бота, дело не в фильтрах, а в том, что модели просто нечего у вас читать.
Комментарии