Как отличить ботов от людей: практическое руководство по классификации посетителей
На сервер приходят не посетители, а запросы. Человек может сделать один просмотр страницы и десяток запросов за ресурсами; поисковый робот может скачать тысячи страниц; ИИ-ассистент может скачать одну страницу, потому что человек задал ему вопрос. Полезный вопрос — не «это человек?», а «что это за клиент и что мне с ним делать?».
Журналы доступа — правильная отправная точка. Они полны в том смысле, в каком аналитика на JavaScript не полна: каждый запрос, дошедший до сервера, там есть, включая роботов, которые никогда не выполняют скрипт. Но при этом журналы скудны. Они записывают адрес, время, путь, статус, referer и заявленный user-agent. Намерение они не записывают.
Коротко. User-agent — это заявление. IP — это место. Cookie — это «может быть». Надёжная классификация получается из независимых признаков, которые согласуются между собой: проверенная сетевая личность, поведение запросов, непрерывность сессии и, там где он оправдывает свою цену, небольшой маячок на JavaScript со своего домена.
Это руководство начинается с того, что может доказать журнал доступа, а затем добавляет признаки, которые может собрать браузер. Оно держит в поле зрения три мысли: проверенный робот — не человек, headless-браузер умеет выполнять JavaScript, и «неизвестно» — допустимый ответ.
В примерах используются диапазоны адресов для документации (203.0.113.0/24, 198.51.100.0/24 и 192.0.2.0/24), так что ни один настоящий посетитель не раскрыт.
Что записывает журнал доступа
Начнём с одной строки из журнала Apache в формате combined:
203.0.113.42 - - [18/Jul/2026:14:22:07 +0300] "GET /catalog/pumps/ HTTP/1.1" 200 18432 "https://example.com/" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
Адрес из диапазона для документации. User-agent заявляет, что это Googlebot. Эта комбинация — вся проблема в одной строке.
| Поле | Пример | Что оно говорит | Где оно обманывает |
|---|---|---|---|
| Адрес клиента | 203.0.113.42 | Владелец сети (её ASN), страна, диапазон дата-центра или домашней сети | NAT, VPN и прокси скрывают человека; CDN скрывает настоящего клиента |
| Отметка времени | 18/Jul/2026:14:22:07 +0300 | Ритм, часовой пояс, рабочие часы | Намерение; робот может работать по человеческому графику |
| Строка запроса | GET /catalog/pumps/ HTTP/1.1 | Метод, путь и протокол | Хотел ли человек эту страницу |
| Статус и байты | 200 18432 | Успех, ошибка, размер ответа | Вовлечённость; 200 может быть страницей-заглушкой |
| Referer | https://example.com/ | Контекст перехода | Правду; его часто нет или он подделан |
| User-Agent | Mozilla/5.0 ... | Заявленную личность клиента | Настоящего клиента; любую строку может написать кто угодно |
Две практические детали важнее, чем кажется. Во-первых, если сайт стоит за CDN или обратным прокси, записанный адрес — это прокси, а не посетитель. Записывайте переданный адрес клиента и непосредственного соседа, и доверяйте переданному заголовку только тогда, когда он пришёл от вашего собственного прокси. Во-вторых, стандартный формат combined не содержит длительности запроса, деталей TLS и большинства заголовков. Добавьте то, что действительно будете использовать, но не записывайте cookie, заголовки авторизации и полные строки запроса, в которых могут оказаться персональные данные.
На Debian или Ubuntu отдельный файл конфигурации Apache держит формат вне виртуального хоста:
/etc/apache2/conf-available/visitor-log.conf
sudo vi /etc/apache2/conf-available/visitor-log.conf
LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" combined_timing
Затем сослитесь на combined_timing в существующей директиве CustomLog. Включите фрагмент и перезагрузите Apache:
sudo a2enconf visitor-log
sudo systemctl reload apache2
%a — адрес клиента, а %D — время запроса в микросекундах. При прокси включите mod_remoteip и перечислите только те прокси, которыми управляете вы. Иначе злоумышленник подделает X-Forwarded-For и выберет личность, которую запишет ваш журнал. Доступные поля описаны в документации Apache: mod_log_config.
В nginx та же оговорка относится к $remote_addr: настройте real_ip со списком доверенных прокси и подумайте, не записывать ли непосредственного соседа и переданный адрес в отдельные поля.
То, чего журнал не видит, так же важно. Он не знает, выполнялся ли JavaScript, заполнял ли форму человек, прочитали ли страницу и вернётся ли посетитель завтра. Запрос к странице — не просмотр страницы. Поэтому классификация по журналу и классификация по браузеру дополняют друг друга, а не соревнуются.
Журнал всё же записывает то, что браузер обычно скачивает: CSS, JavaScript, картинки, шрифты и favicon. Их наличие или отсутствие — довод. Не доказательство. Держите это различие до конца руководства.
Проверка на реальных данных 2026 года
В июле 2026 года Валентина Меланина опубликовала трёхдневный аудит настоящего магазина оборудования для бизнеса: 16 705 запросов, 39 % из них автоматические, и каждая строка сверена с исходным IP. Проценты — полезная калибровка, а не всеобщий закон.
| Заявленный токен | Запросов | Подтверждено | Что нашёл аудит |
|---|---|---|---|
| Googlebot | 896 | 268 | Около 70 % пришли не из сетей Google |
| Bytespider | 1 073 | 0 | Арендованные адреса AWS в Сингапуре, а не ByteDance |
| ChatGPT-User | 265 | 237 | 71 IP, 23 сессии; живые запросы по инициативе пользователя |
| Bingbot | 467 | 467 | В этой выборке подтверждён полностью |
Числа — не вся история. Два адреса Google Cloud заявили по пятнадцать разных личностей ботов, один адрес Cloudflare — одиннадцать. Один сервер — это не пятнадцать компаний, это сканер или проверяльщик доступа для ИИ, который меняет маски. 237 подтверждённых запросов ChatGPT-User пришли с 71 IP и образовали 23 сессии: люди задавали ChatGPT вопрос и вызывали живую загрузку. Веб-аналитика их не увидела. Отдельно 57 запросов ушли на служебные пути вроде /.git, /.ssh, /.env, /secrets.json и /actuator, все под именами ИИ-ботов. Это были сканеры в модной маске.
Метод аудита воспроизводим: сгруппировать по user-agent, выписать IP, сверить каждый IP с текущими диапазонами поставщика или с обратным и прямым подтверждением DNS, затем читать оставшиеся запросы как трафик роботов. Полный разбор — melanina.ru.
Важны два урока. Первый: известное имя — маска, а не личность. Второй: ИИ-трафик — не одно и то же. GPTBot — робот, который собирает данные для обучения. OAI-SearchBot — робот поискового индекса. ChatGPT-User — загрузка, вызванная вопросом человека. Последняя запущена человеком, но выполнена машиной: это не человеческий просмотр страницы и не обычный шум ботов. Схема классификации, в которой есть только «бот» и «человек», разметит его неверно.
Для более широкой базы: исследование 2026 года по более чем 4,6 млн запросов показало, что автоматический трафик — примерно половина всех веб-запросов. Точная доля зависит от сайта, недели и того, что вы считаете. Метод важнее заголовка.
Проверяйте имя, а не только user-agent
Фильтрация по известным именам — полезный первый проход. Она дешёвая, ловит честную автоматизацию и даёт словарь, с которым можно работать. Но это не проверка личности. Любой может отправить любую строку в User-Agent; руководство MDN по определению браузера по той же причине советует проверять возможности, а не разбирать UA.
Используйте две независимые проверки:
- Опубликованные диапазоны IP. Поставщик публикует JSON или блоки CIDR (диапазоны адресов) для своих роботов. Сверьте адрес из журнала с текущим списком.
- Обратное и прямое подтверждение DNS (FCrDNS). Сделайте обратный запрос адреса, проверьте, что имя хоста принадлежит домену поставщика, затем прямой запрос имени и проверьте, что он возвращает тот же адрес.
Одного обратного DNS мало. Кто управляет адресом, тот может поставить в его PTR-запись известное имя. Проверяемым заявление делают прямой запрос и проверка домена. Для Google имя хоста должно заканчиваться на googlebot.com, google.com или googleusercontent.com — в зависимости от типа робота.
host 66.249.66.1
1.66.249.66.in-addr.arpa domain name pointer crawl-66-249-66-1.googlebot.com.
host crawl-66-249-66-1.googlebot.com
crawl-66-249-66-1.googlebot.com has address 66.249.66.1
Первый запрос даёт имя; второй доказывает, что имя ведёт обратно. Поддельная PTR-запись пройдёт первую проверку, но не вторую, если только злоумышленник не управляет ещё и прямым DNS домена, которым не владеет, — поэтому проверка домена и важна.
| Оператор | Способ проверки | Что проверять |
|---|---|---|
| Пять файлов IP по типам роботов + FCrDNS | googlebot.com для обычных роботов; имена googleusercontent.com или google-proxy для загрузок по инициативе пользователя | |
| Microsoft Bing | Список IP + инструмент Verify Bingbot + FCrDNS | Обратные имена заканчиваются на search.msn.com; список обновляйте каждый день |
| OpenAI | Отдельный файл IP на каждого агента | GPTBot, OAI-SearchBot, ChatGPT-User, OAI-AdsBot; опубликованного способа проверки rDNS нет |
| Anthropic | Один общий файл IP | Список не различает ClaudeBot, Claude-User и Claude-SearchBot |
| Perplexity | Два файла IP | PerplexityBot и Perplexity-User |
| Apple | Список IP + FCrDNS | Обратные имена заканчиваются на *.applebot.apple.com |
| Common Crawl | Список IP + FCrDNS | crawl.commoncrawl.org; обратный DNS для IPv6 не поддерживается |
| Amazon | Три страницы с IP | Amazonbot, Amzn-SearchBot, Amzn-User |
| Meta | Опубликованного способа проверки нет | На октябрь 2026 заявление meta-externalagent со страницы Meta проверить нельзя |
| Yandex | FCrDNS | Обратные имена заканчиваются на spider.yandex.com; используйте любой текущий список IP, который публикует поставщик |
Списки меняются. Забирайте их у поставщика в тот день, когда используете; не копируйте блоки CIDR из статьи, в том числе из этой. Текущие инструкции и файлы Google — на странице Verify requests from Google; файлы и смысл отдельных агентов OpenAI — в её документации по роботам; Anthropic публикует один общий список; Perplexity описывает оба файла в своём руководстве по ботам.
Два имени в журнале не появятся никогда: Google-Extended и Applebot-Extended — это токены управления robots.txt, а не роботы. Если панель отчитывается о визитах от них, панель ошибается.
Проект ai.robots.txt — полезный словарь имён ИИ-роботов и готовая политика robots.txt. Используйте его, чтобы найти токены, а затем проверьте IP; сам список — не доказательство.
Программа Verified Bots Cloudflare и появляющиеся подписи Web Bot Auth — третий путь: провайдер на периметре или криптографическая подпись ручается за клиента. Они полезны, когда вы уже стоите за этим провайдером, но универсальными не являются.
| Заявленный токен | Что это | Считать посетителем? |
|---|---|---|
| Googlebot, Bingbot, YandexBot | Роботы поисковых систем | Нет |
| GPTBot, ClaudeBot, CCBot, Bytespider | Роботы, собирающие данные для обучения | Нет |
| OAI-SearchBot, PerplexityBot | Роботы поискового индекса ИИ | Нет |
| ChatGPT-User, Perplexity-User, Claude-User | Загрузка по инициативе пользователя | Начато человеком, но это не просмотр страницы |
| curl, python-requests, Go-http-client, Scrapy, axios, HeadlessChrome | Заявленная автоматизация | Скорее всего нет, но проверьте |
| Мониторы доступности, предпросмотр ссылок, SEO-роботы | Автоматические сервисы | Нет |
Проверенный робот — всё равно робот. Законность не делает его человеком. Держите verified_crawler отдельно от human в своей схеме, а unverified — отдельно от fake. Неудачная проверка значит «я не смог это подтвердить», а не «я доказал, что это мошенничество».
Поведение, которое оставляет след
Проверка личности ловит ботов, которые себя объявляют. Поведение ловит ботов, которые врут. Журнал даёт последовательность запросов, и эта последовательность часто информативнее любой отдельной строки.
| Признак | Как выглядит | Сила |
|---|---|---|
| Доля ресурсов | Бот: только HTML или одни и те же фиксированные ресурсы. Человек: CSS, JS, картинки и шрифты, неравномерно. | Средняя |
| Ритм сессии | Бот: ровные промежутки, мало разброса. Человек: всплески, паузы и возвраты. | Средняя |
| Форма навигации | Бот: порядок карты сайта, последовательные ID, глубокий обход. Человек: вход из поиска или соцсети, чтение, возвраты. | Средняя |
| Методы HTTP | Бот: HEAD, OPTIONS или TRACE; POST на HTML-страницы. Человек: GET и POST на настоящих формах. | Высокая для сканеров |
| Пробы ошибок | Бот: /.env, /.git, wp-login.php, /actuator. Человек: изредка битая ссылка. | Высокая для сканеров |
| Несколько личностей с одного IP | Бот: больше 3–5 user-agent с одного IP за короткое окно. Человек: обычно один или два. | Высокая |
| Непрерывность cookie | Бот: cookie нет или они случайны на каждом запросе. Человек: стабильная сессионная cookie между страницами. | Средняя |
| Referer | Бот: нет, указывает на себя или поддельный. Человек: поиск, соцсеть или внутренний переход. | От низкой до средней |
| Распределение по времени | Бот: ровное круглые сутки. Человек: местные рабочие часы. | Сама по себе низкая |
| Тип сети | Бот: облако или хостинг по ASN. Человек: домашняя или мобильная сеть, с оговоркой про VPN. | Средняя |
| Favicon и robots.txt | Бот: часто пропускает favicon, часто просит robots.txt. Человек: обычно наоборот. | Низкая |
Доля ресурсов и отрисовка
Браузер скачивает HTML, затем CSS, JavaScript, шрифты и картинки. Простой HTTP-клиент часто скачивает только HTML. Высокая доля HTML к ресурсам поэтому — слабый признак бота. Но Googlebot может скачивать ресурсы, когда отрисовывает страницу, а человек может их блокировать. Исследование 2026 года Shy Guys по 4,6 млн запросов обнаружило, что боты часто пропускают favicon; запрос favicon — слабый довод в пользу браузера, но не доказательство человека.
Считайте долю по сессии, а не по IP, когда можете: static_requests / html_requests. Сессия с 40 запросами HTML и без CSS, JavaScript и картинок — не читатель. Сессия с одним запросом HTML и без ресурсов может быть предзагрузкой или текстовым браузером.
Ритм
Боты часто ровные; люди — всплесками. Для каждой сессии возьмите промежутки между запросами и посчитайте коэффициент вариации: CV = stdev(gaps) / mean(gaps). CV около нуля на 20 и более запросах — машинный. CV больше 1 для людей обычен. Но вежливый робот со случайными задержками может выглядеть как человек, а панель с автообновлением — как ровный бот. Используйте ритм вместе с объёмом и формой путей, а не отдельно.
«Запросов в минуту» — плохой самостоятельный признак: робот может быть медленным, а человек может открыть тридцать вкладок. Рисунок промежутков информативнее среднего.
Форма навигации
Парсеры перечисляют. Они идут в порядке карты сайта, по последовательным ID товаров, по страницам категорий и ссылкам «следующая страница». Люди приходят из поиска или соцсети, читают, возвращаются, открывают новую вкладку и уходят. Сессия, которая начинается с глубокого URL и проходит двести страниц идеальной последовательностью, — не читатель.
Пробы ошибок — самая резкая версия этого признака. /.env, /.git, /wp-login.php и /actuator — не контент. Их скопление — подпись сканера, даже когда user-agent говорит «Chrome».
Две быстрые проверки, прежде чем писать парсер. В формате combined первое поле — адрес, девятое — статус:
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -10
Пример вывода:
1842 66.249.66.1 913 203.0.113.42 407 198.51.100.7
Счётчики — не классификация. Большое число может быть Googlebot за работой или парсером, притворяющимся Googlebot.
awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -nr | head -10
Пример вывода:
57 /.env 31 /.git/config 18 /wp-login.php 12 /actuator/env
Эти пути — подписи сканеров. Они дают высокую уверенность, даже когда остальной запрос выглядит как браузер.
Несколько личностей с одного IP
Сгруппируйте по IP и посчитайте разные строки user-agent за короткое окно. Больше трёх — подозрительно; аудит 2026 года нашёл два облачных IP, каждый из которых заявлял пятнадцать личностей. Общий корпоративный прокси, CDN или развёртывание обновления браузера может дать несколько законных user-agent, поэтому требуйте объёма и короткого окна. Это один из самых дешёвых ценных признаков в журнале.
Непрерывность cookie и сессии
Настоящий браузер обычно сохраняет сессионную cookie своего домена между страницами. Парсер часто выбрасывает cookie или получает новую сессию на каждом запросе. Но браузеры приватности, баннеры про cookie и API без состояния это ломают. Проверяйте, возвращается ли та же cookie по последовательности, а не есть ли cookie вообще.
Чтобы считать признаки по IP, а не прикидывать на глаз, сохраните этот скрипт как features.py. Он читает журнал combined, пропускает некорректные строки и печатает верхние адреса по числу запросов с долей ресурсов, долей ошибок и числом разных user-agent:
features.py
vi features.py
#!/usr/bin/env python3 """Print per-IP request, static, error, and user-agent counts.""" from __future__ import annotations import re import sys from collections import Counter, defaultdict LINE = re.compile( r'^(?P<ip>\S+) \S+ \S+ \[[^\]]+\] ' r'"(?P<method>\S+) (?P<path>\S+) [^"]+" ' r'(?P<status>\d{3}) \S+ "[^"]*" "(?P<agent>[^"]*)"' ) STATIC = (".css", ".js", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".woff2") def main(path: str) -> None: """Read a combined log and print the top IPs by request count.""" requests: Counter[str] = Counter() static: Counter[str] = Counter() errors: Counter[str] = Counter() agents: defaultdict[str, set[str]] = defaultdict(set) with open(path, encoding="utf-8", errors="replace") as log: for line in log: match = LINE.match(line) if not match: continue row = match.groupdict() ip = row["ip"] requests[ip] += 1 agents[ip].add(row["agent"]) if row["path"].lower().endswith(STATIC): static[ip] += 1 if row["status"].startswith(("4", "5")): errors[ip] += 1 for ip, count in requests.most_common(20): print( f"{ip:16} req={count:5d} " f"static={static[ip] / count:.2f} " f"errors={errors[ip] / count:.2f} " f"uas={len(agents[ip])}" ) if __name__ == "__main__": main(sys.argv[1] if len(sys.argv) > 1 else "access.log")
Запустите его на журнале:
python3 features.py access.log
Примерный вывод:
203.0.113.42 req= 913 static=0.04 errors=0.01 uas=1 66.249.66.1 req= 1842 static=0.02 errors=0.00 uas=1 198.51.100.7 req= 407 static=0.00 errors=0.38 uas=15
Один адрес — робот с большим объёмом и почти без ресурсов; у другого пятнадцать user-agent и 38 % ошибок. Скрипт ничего не решает. Он даёт признаки для шага оценки.
Что может добавить JavaScript
Журналы видят запрос; JavaScript видит клиента. В этом и обмен. JavaScript может наблюдать окружение и взаимодействие, но только у клиентов, которые его выполняют. Большинство роботов не выполняют; headless-браузер выполняет. Отсутствие маячка — сильный довод в пользу клиента без JavaScript. Наличие маячка — слабый довод в пользу человека.
Три слоя стоит собирать, именно в этом порядке:
- Существование и время. Выполнилась ли страница, когда, сколько и была ли она видима?
- Окружение и согласованность. Сходится ли самоописание браузера с заголовками запроса и с самим собой?
- Взаимодействие. Было ли событие указателя, прокрутки, клавиши или фокуса?
Начните с небольшого маячка со своего домена. Не начинайте со снятия отпечатка через canvas: его труднее оправдать, легче сломать, и он менее полезен, чем знание, выполнилась ли страница вообще.
Минимальный маячок
Отрисуйте на сервере случайный ID визита в страницу. Это ключ соединения, а не секрет:
<meta name="visit-id" content="a7f3c9e2">
Затем отправьте три небольших события на свой эндпоинт: загрузку, «жив» через пять секунд и скрытие, когда страница становится скрытой. Этот фрагмент намеренно записывает, было ли взаимодействие, а не куда ушёл указатель и что было набрано:
<script> (() => { const visitId = document.querySelector('meta[name="visit-id"]')?.content ?? ""; const endpoint = "/api/visit"; const started = performance.now(); const storageWorks = () => { try { localStorage.setItem("__probe", "1"); localStorage.removeItem("__probe"); return true; } catch { return false; } }; const base = () => ({ visit_id: visitId, path: location.pathname, referrer: document.referrer, screen: `${screen.width}x${screen.height}`, viewport: `${innerWidth}x${innerHeight}`, language: navigator.language, languages: navigator.languages?.join(",") ?? "", timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, webdriver: navigator.webdriver === true, cores: navigator.hardwareConcurrency ?? 0, memory: navigator.deviceMemory ?? 0, touch: navigator.maxTouchPoints ?? 0, cookies: navigator.cookieEnabled, storage: storageWorks(), visibility: document.visibilityState, }); const send = (event, extra = {}) => { const body = JSON.stringify({ ...base(), event, ...extra }); if (navigator.sendBeacon) { navigator.sendBeacon(endpoint, new Blob([body], { type: "application/json" })); } else { fetch(endpoint, { method: "POST", body, keepalive: true, headers: { "Content-Type": "application/json" }, }); } }; const onLoad = () => send("load", { load_ms: Math.round(performance.now()) }); if (document.readyState === "complete") onLoad(); else addEventListener("load", onLoad, { once: true }); let interacted = false; for (const name of ["pointermove", "scroll", "keydown"]) { addEventListener(name, () => { interacted = true; }, { once: true, passive: true }); } setTimeout(() => send("alive", { interacted, elapsed_ms: Math.round(performance.now() - started), }), 5000); addEventListener("visibilitychange", () => { if (document.visibilityState === "hidden") { send("hide", { interacted, elapsed_ms: Math.round(performance.now() - started), }); } }); })(); </script>
- Используйте свой эндпоинт. sendBeacon отправляет и забывает и лучше переживает выгрузку страницы, чем обычный запрос; запасной вариант на fetch использует keepalive. Проверяйте тело запроса, ограничьте частоту и размер тела. Возвращайте 204.
- ID визита — ключ соединения, а не аутентификация. Генерируйте его на сервере, меняйте и никогда не кладите в него персональные данные.
- Записывайте, было ли взаимодействие, а не что набрали и куда ушёл указатель. Это осознанный выбор в пользу приватности. Сырые нажатия и координаты для классификации не нужны.
- Бот может выполнить этот код и подделать любое поле. Маячок — довод, а не шлюз.
О чём говорят поля
| Признак | О чём говорит | Как ошибается |
|---|---|---|
| Нет маячка через 10 секунд | Нет JavaScript, скрипт заблокирован или простой HTTP-клиент | У пользователя отключён JavaScript; бот выполняет JavaScript |
| webdriver: true | Фреймворк автоматизации оставил флаг включённым | Флаг можно пропатчить; законная автоматизация тоже его ставит |
| Пустые languages или plugins | Headless- или урезанное окружение | Браузеры приватности их сокращают; значения можно подделать |
| touch: 0 при мобильном user-agent | Несогласованное заявление | Бывает режим компьютера, планшеты и гибридные устройства |
| Часовой пояс не совпадает со страной IP | Прокси или несовпадающее окружение | VPN, поездки и эмигранты |
| Нет взаимодействия через 5 секунд | Загрузка скриптом | Пользователь открыл страницу в фоновой вкладке; бот может синтезировать события |
| storage: false | Хранилище заблокировано или окружение ограничено | Настройки приватности и ограничения iframe |
| Быстрая загрузка и сразу скрытие | Предзагрузка или бот | Настоящие пользователи тоже уходят сразу |
| Хеш canvas или WebGL | Личность устройства или автоматизации | Подделывается, шумит и много требует от приватности |
| isTrusted: false | Синтетическое событие | CDP умеет создавать доверенные события; это не стена |
navigator.webdriver — стандартное свойство, которое говорит, что user agent управляется автоматизацией. Это один полезный пример, а не приговор: MDN описывает его как признак, а современная автоматизация умеет его подавлять. То же относится к отсутствующим плагинам, пустым языкам и «headless-подобным» экранам. Статья F5 2015 года про PhantomJS — каталог пробелов окружения; конкретные приметы изменились, а принцип нет. Считайте каждую из них слабым признаком.
Снятие отпечатка идёт дальше: canvas, WebGL, звук, шрифты, рукопожатие TLS, порядок заголовков. Оно может быть мощным, но оно вероятностное, хрупкое и вторгается в приватность. Руководство W3C по снятию отпечатков считает это риском для приватности; руководство ICO по PECR считает доступ к хранилищу и устройству тем, что обычно требует согласия. Для обычной аналитики не делайте этого. Для защиты от мошенничества или безопасности опишите цель, минимизируйте данные, храните их недолго и получите юридическую проверку. Отпечаток — не человек; это оценка похожести.
Отпечатки транспорта (JA3/JA4) — сильный признак несовпадения: запрос, который заявляет Chrome, но договаривается о TLS как скрипт на Python, — не Chrome. Они требуют доступа на терминаторе TLS или CDN, который отдаёт отпечаток; в обычном журнале доступа их нет. Добавляйте их, только если управляете этим слоем.
Проверочные задачи (challenge) — это шаг, а не фундамент. Доказательство работы на JavaScript или CAPTCHA поднимают цену, но CAPTCHA обходится и мешает доступности. Используйте их для неоднозначных сессий, после более дешёвых проверок, и всегда оставляйте путь для тех проверенных роботов, которые вам нужны. Обзор методов обнаружения ботов от Azion описывает тот же порядок: репутация, проверка известных хороших, отпечаток, поведение, затем проверочная задача или блокировка.
Это гонка вооружений, и она несимметрична. Руководство по автоматизации Browserless 2026 года открыто описывает скрытные маршруты, которые убирают navigator.webdriver и вносят энтропию в canvas, WebGL и звук, домашние прокси, которые чинят репутацию IP, и решение CAPTCHA. Если ваш сайт — ценная цель, исходите из того, что упорный клиент пройдёт любую проверку на стороне клиента. Цель — не непобедимая стена, а сделать дешёвую атаку неудачной, а дорогую отправить на проверочную задачу или к человеку. Смотрите Browserless об обнаружении ботов.
Современные ИИ-агенты ещё сильнее усложняют слой браузера. Некоторые управляют настоящим Chrome, используют домашние соединения и подавляют navigator.webdriver; они могут пройти каждую отдельную проверку выше. Руководство cside 2026 года утверждает, что самый надёжный признак — поведение: детерминированные траектории мыши, чтение DOM всплесками, артефакты Chrome DevTools Protocol (CDP), отсутствие прокрутки до запроса. Оно же предлагает классифицировать намерение, а не блокировать по двоичному признаку, потому что потребительский агент может пытаться что-то купить. Здесь та же логика: оцените сессию, назовите намерение и выберите действие для каждого маршрута. Смотрите руководство cside по ИИ-агентам.
От признаков к решению
Оценка складывает слабые доводы. Веса ниже — пример, а не стандарт. Откалибруйте их на своей размеченной выборке; структура важнее чисел.
| Признак | Пример веса | Почему |
|---|---|---|
| Проверенный робот (список IP или FCrDNS) | Переопределение в verified_crawler | Твёрдая положительная личность; не добавляйте баллы известному боту |
| Известное имя бота, проверка не прошла | +40 | Выдача себя за другого или непроверяемое заявление |
| Проба служебного пути (/.env, /.git, /actuator) | Переопределение в scanner | Намерение не про контент |
| Больше 3 user-agent с одного IP за 10 минут | +25 | Сканер с несколькими личностями |
| Дата-центр или хостинг по ASN | +15 | Не решает: бывают VPN, облачные рабочие столы и CDN |
| Сессия только с HTML, 10+ запросов | +15 | Ресурсы не скачивались |
| CV промежутков < 0.3 на 20+ запросах | +15 | Машинный ритм |
| Доля 4xx > 30 % на 10+ запросах | +15 | Поведение сканера |
| Нет cookie между страницами | +10 | Клиент без состояния |
| Маячок JS с событием взаимодействия и 5 с на странице | -30 | Слабый довод в пользу человека |
| Согласованное окружение, cookie и ресурсы | -20 | Связная сессия браузера |
Категории, а не двоичный признак
- verified_crawler — поисковый, ИИ-поисковый или обучающий робот, подтверждённый списком IP или FCrDNS.
- declared_automation — curl, python-requests, мониторинг или SEO-инструменты; честно заявляют, что они автоматические.
- scanner — пробы служебных путей, сканирование уязвимостей, много 4xx на чувствительных путях.
- likely_automated — высокая оценка по совокупности слабых признаков.
- uncertain — доводов мало или они противоречат друг другу.
- human_initiated_agent — ChatGPT-User, Perplexity-User или Claude-User; человек спросил, машина скачала.
- likely_human — связный браузер, непрерывность сессии и взаимодействие.
Порядок важен. Сначала переопределения, затем складываемые признаки, затем остаточные категории. Проверенного робота не должен «спасать» маячок JavaScript, а сканера не должен выручать браузерный user-agent. И наоборот: человека с отключённым JavaScript не стоит относить к ботам только потому, что маячка нет.
| Задача | Действие | Чего не делать |
|---|---|---|
| Аналитика | Исключить проверенных роботов, сканеров и вероятную автоматизацию; оставить uncertain; агентов по инициативе человека показывать отдельно. | Не выдавать «посетителей» за людей, когда в число входят роботы. |
| Ограничение частоты | Для автоматических категорий пороги ниже; проверенным поисковым и ИИ-поисковым роботам дать те лимиты, которые вы задумали. | Не загонять проверенного робота в 429, которых вы не хотели. |
| Контроль доступа | Неоднозначные сессии отправлять на проверочную задачу; блокировать только сканеров с высокой уверенностью или вредное поведение. | Не блокировать неопределённые сессии и целые облачные ASN. |
| Видимость в ИИ | Разрешать нужных ИИ-роботов; проверять, что они получают 200, а не 403 или 429. | Не блокировать каждый ИИ-токен рефлекторно; агенты по запросу пользователя могут приводить клиентов. |
Оценивайте классификатор, а не только оценку. Разметьте вручную от 200 до 500 сессий, стратифицировав их по полосам оценки. Считайте точность и полноту по каждой категории, а не одно общее число правильных ответов. Следите за ложными срабатываниями: средства доступности, корпоративные прокси, браузеры приватности, текстовые браузеры, предпросмотр ссылок и мониторы доступности — обычные жертвы. Перекалибруйте, когда меняется трафик или боты.
Держите корзину uncertain. Это не провал, это честность. Классификатор, который никогда не говорит «неизвестно», переоценивает свои силы, а жёсткая блокировка неопределённой сессии — это путь, которым ложные срабатывания становятся обращениями в поддержку.
Приватность, ложные срабатывания и правила
Журналы и маячки обрабатывают персональные данные. IP-адрес может быть персональными данными; cookie или отпечаток устройства может быть онлайн-идентификатором. В ЕС и Великобритании PECR/ePrivacy требует согласия на хранение информации на устройстве пользователя или доступ к ней, с ограниченными исключениями, а к обработке применяется GDPR. Руководство ICO про cookie и похожие технологии — отправная точка, а не юридическая консультация.
Минимизируйте то, что собираете:
- Записывайте только те поля, которые используете; избегайте cookie, заголовков авторизации и полных строк запроса.
- Усекайте или хешируйте IP-адреса после срока хранения; сырые журналы держите дни, агрегаты — дольше.
- Ограничьте доступ, шифруйте данные на диске и опишите цель, срок хранения и доступ.
- Соблюдайте Global Privacy Control там, где он применяется; уведомление о приватности должно совпадать с тем, что вы действительно делаете.
- Для снятия отпечатков проведите оценку воздействия на защиту данных или юридическую проверку, раскройте её и предпочитайте цель безопасности, а не маркетинга.
Ложные срабатывания — не пограничные случаи:
- Программы чтения с экрана и пользователи только клавиатуры могут не давать движения указателя.
- NoScript и текстовые браузеры не дают маячка JavaScript.
- Корпоративные прокси, VPN и мобильные операторы делят IP между многими.
- Браузеры приватности сокращают признаки canvas, языков и плагинов.
- Предпросмотр ссылок, RSS-читалки и мониторы доступности скачивают страницы без человека.
- Отрисовщики поисковых систем скачивают ресурсы и выполняют JavaScript.
Проектируйте с учётом этого. Жёсткая блокировка по признаку «нет JavaScript» заденет некоторых людей. Блокировка по ASN дата-центра заденет некоторых клиентов. Сначала проверочная задача, а блокировка — только при плохом поведении, в котором вы уверены, и дайте людям способ сообщить о ложном срабатывании.
Правила — отдельное решение, не часть обнаружения:
- robots.txt — пожелание, а не стена. Он работает для послушных роботов; парсер он не остановит. Robots Exclusion Protocol — не разрешение на доступ.
- Решите, какие боты вам нужны — поисковые, ИИ-поисковые, обучающие, SEO, мониторинг — и опишите это решение.
- ai.robots.txt — полезная отправная точка для политики по ИИ, а не средство защиты.
- Не блокируйте по стране или по целому облачному ASN; вы заденете законных пользователей.
- Ведите журнал аудита: время, версия правила, код причины, действие. Раз в неделю просматривайте заблокированные запросы.
Рабочий порядок действий
Начните с одной недели журналов и одного вопроса: сколько запросов сделали не люди? Работающая последовательность:
- Наведите порядок в формате журнала. Убедитесь, что настоящий IP клиента переживает CDN или прокси. Записывайте user-agent, referer, статус, байты и время; добавьте длительность, если можете.
- Соберите словарь проверенных ботов. Для каждого токена: поставщик, назначение, URL списка IP, суффикс rDNS и дата последней проверки. Обновляйте списки ежедневно или еженедельно.
- Разбейте на сессии. Окно неактивности 30 минут. Предпочитайте cookie своего домена; при её отсутствии опирайтесь на IP плюс user-agent плюс время.
- Обогатите. Добавьте ASN, тип сети, rDNS, грубую геолокацию, семейство user-agent и класс пути.
- Посчитайте признаки. Доля ресурсов, доля ошибок, число разных user-agent, ритм, глубина, входной путь и непрерывность cookie.
- Разметьте выборку. Начните с проверенных роботов и очевидных сканеров; затем размечайте неоднозначные сессии, не глядя на оценку.
- Оцените и задайте пороги. Отделите аналитику от контроля доступа. Оставьте корзину uncertain и путь для обжалования.
- Добавьте маячок. Начните с части страниц. Соединяйте по ID визита или по IP, user-agent и времени. Измерьте, что он добавляет, прежде чем включать его везде.
- Смотрите и перекалибруйте. Первый месяц — раз в неделю, затем раз в месяц или квартал. Следите за ложными срабатываниями и заблокированными роботами.
Шаблон недельного отчёта, который держит категории честными:
category requests share verified search crawler ... ... verified AI crawler ... ... declared automation ... ... scanner ... ... likely automated ... ... uncertain ... ... human-initiated agent ... ... likely human ... ...
Заполняйте его тем же классификатором, который принимает решения. Не ведите вторую таблицу с другим определением «человека».
Если делаете только одно — проверьте IP, прежде чем доверять имени. Если делаете два — добавьте проверку проб служебных путей. Оба дешёвые, и вместе они убирают большую часть шума.
Источники и что почитать дальше
Статьи ниже легли в основу этого руководства. Списки поставщиков и способы проверки меняются; сверяйтесь с текущей документацией, прежде чем полагаться на диапазон или токен.
Журналы, поведение и картина 2026 года
- Tell bots apart from human visitors for stats? — Stack Overflow; классическое обсуждение user-agent, невидимых ссылок, загрузки ресурсов и их пределов.
- Кто на самом деле сканирует ваш сайт в 2026 году — трёхдневный аудит Валентины Меланиной; источник чисел 39 %, 70 % и 237.
- Как распознать ботов на сайте через заголовки сервера — vc.ru; практические признаки, включая пятисекундную проверку активности.
- Shy Guys: A Light-Weight Approach to Detecting Robots on Websites — arXiv, 2026; 4,6 млн запросов, эвристики по favicon и цель в 3 % ложных срабатываний.
- Методы обнаружения ботов — Azion; поведенческий, отпечаточный, слои проверочных задач и IP, машинное обучение, с порядком «сначала проверочная задача».
- Как обнаружить ИИ-агентов на своём сайте — cside; слои личности, сети, браузера и поведения для агентов в браузере.
JavaScript и снятие отпечатков
- Обнаружение ботов с помощью JS-тега — WorkOS; что может собрать тег JavaScript и где он останавливается.
- Отпечаток браузера и обнаружение ботов — Web Scraper; отпечатки как довод, а не личность, с различением пассивного и активного снятия отпечатков у W3C.
- Обнаружение ботов: как это работает — Browserless; сторона автоматизации в гонке вооружений, включая скрытные отпечатки и домашние прокси.
- Обнаружение посетителей на PhantomJS — F5; исторический каталог примет headless-браузера и их пределов.
- Navigator.webdriver — MDN; стандартный флаг автоматизации и почему он не граница безопасности.
- Определение браузера по строке user agent — MDN; почему разбор UA ненадёжен и почему лучше проверять возможности.
Проверка и реализация
- Проверка запросов от Google — Google; FCrDNS и текущие файлы IP.
- Обзор роботов OpenAI — OpenAI; файлы IP по агентам и разница между GPTBot, OAI-SearchBot и ChatGPT-User.
- Собирает ли Anthropic данные из веба? — Anthropic; общий список IP роботов.
- Документация по ботам — Perplexity; проверка PerplexityBot и Perplexity-User.
- Как проверить настоящих ИИ-ботов в журналах сервера — eSEOspace, октябрь 2026; таблица проверки по поставщикам и замечание про Google-Extended/Applebot-Extended.
- Проверенные боты — Cloudflare; Web Bot Auth и программа проверенных ботов.
- mod_log_config — Apache; поля формата журнала и свои форматы.
- Cookie и похожие технологии — ICO; руководство PECR о согласии на хранение и доступ к устройству.
- Руководство по снятию отпечатков — W3C; пассивное и активное снятие отпечатков и риски для приватности.
- ai.robots.txt — список токенов ИИ-роботов и политики robots.txt от сообщества.
Ссылки на Stack Overflow, Меланину, vc.ru, WorkOS, Azion, Web Scraper и cside — источники, предложенные для этой статьи; остальные — первичные документы поставщиков и стандартов, по которым проверялись утверждения.
Автор статьи: Arthur Isaev