Назад в Центр знаний
Идентичность

User-Agent и Client Hints: одна согласованная идентичность

Как заголовок User-Agent, Client Hints и navigator.userAgentData должны совпадать, почему подмена в фреймворке оставляет разрывы и как задать и проверить свою идентичность.

BotBrowser Team

Документация

Нужна поддерживаемая продуктовая документация?

У этой статьи есть соответствующая страница в центре документации. Используйте docs для каноничного сценария настройки, актуальных флагов и долгосрочной справки.

Собственный User-Agent задают так, чтобы строка, заголовки Client Hints, navigator.userAgentData и контекст Worker описывали один браузер. BotBrowser задаёт согласованную идентичность через --user-agent и флаги --bot-*, но не добавляет браузеру возможности, которых нет у базовой сборки.

Краткое резюме

Меняйте строку только вместе с бренд-списком, версиями и полями платформы, иначе заголовок, Sec-CH-UA и userAgentData разойдутся. Проверяйте идентичность в трёх местах: на странице, в воркере и в заголовках запроса.

Содержание руководства

Почему заголовок User-Agent, Client Hints и userAgentData должны совпадать

Заголовок User-Agent является самым старым способом, которым браузер описывает себя сайту. Долгие годы это была длинная строка с названием браузера, полной версией, операционной системой и движком отрисовки. С тех пор Chrome сократил объем сведений в этой строке, а структурированные данные перешли в User-Agent Client Hints, обычно сокращаемые до UA-CH. В результате одна идентичность браузера теперь выражена сразу в нескольких местах: в заголовке запроса User-Agent, в семействе заголовков запроса Sec-CH-UA, в строке navigator.userAgent и в API navigator.userAgentData. BotBrowser может сохранять согласованными строку User-Agent, Client Hints и значения navigator одного профиля в контролируемом контексте, но не меняет того, как сервер поступает с этой идентичностью.

Поэтому собственная идентичность настолько надежна, насколько надежна ее самая слабая поверхность. Если изменить только строку, заголовок скажет одно, Sec-CH-UA другое, а navigator.userAgentData третье. Каждая поверхность корректна сама по себе, но вместе они описывают браузер, которого не существует.

Это важно и для приватности, и для обычной работоспособности. Заголовок User-Agent и подсказки с низкой энтропией приходят с первым запросом, до выполнения любого скрипта, так что сервер видит их раньше, чем загрузится страница. Страницы и скрипты аналитики затем могут снова прочитать те же факты через JavaScript и сравнить их. Если ответы различаются, сама эта комбинация необычна и делает сеанс легче выделить. Она же вызывает обычные поломки, например когда сервер выбирает мобильную верстку по заголовку User-Agent, а скрипт читает из navigator.userAgentData настольную платформу.

Схема: одна заданная идентичность определяет HTTP-заголовки, основной поток, контекст Worker и значения высокой энтропии, после чего проверка подтверждает, что все четыре совпадают

С этой проблемой чаще всего сталкиваются три группы читателей. Команды, которые проверяют сайт для посетителей Android или Edge, должны видеть, что браузер везде описывает себя как такого посетителя. Люди, которые ведут несколько раздельных браузерных идентичностей ради приватности, должны добиться, чтобы каждая идентичность оставалась стабильной и внутренне согласованной. Инженерам поддержки, которые воспроизводят обращение клиента, нужно, чтобы воспроизведение несло ту же идентичность, что была у клиента. Для всех троих важно совпадение поверхностей, а не одно отдельное значение.

Прежде чем что-либо менять, полезно задать три вопроса:

  • Какие поверхности сообщают идентичность и изменятся ли они все вместе?
  • Относятся ли все номера версий к одной основной версии Chromium?
  • Описывает ли остальная конфигурация, такая как локаль, часовой пояс и сетевой маршрут, по-прежнему один целостный браузер?

Поверхности, которые должны совпадать

Браузеры на основе Chromium описывают себя четырьмя группами значений. Каждая из них разобрана ниже, а затем приведен короткий список совпадений, которые можно проверить.

Строка User-Agent

Заголовок запроса User-Agent и navigator.userAgent несут одну и ту же строку. В настольном Chrome она выглядит так:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.7444.60 Safari/537.36

При сокращении User-Agent в Chrome части этой строки с операционной системой и версией удерживаются на обобщенных значениях, а младшие части версии зафиксированы. Строка по-прежнему отправляется с каждым запросом и остается первым, что читает большинство серверов, поэтому она должна согласовываться со структурированными значениями, которые заменили утраченные подробности. О стандартах и пошаговом переходе от разбора User-Agent к Client Hints см. Миграция от User-Agent Reduction к Client Hints.

Заголовки запроса Client Hints

Client Hints передают те же факты в структурированном виде. Подсказки с низкой энтропией отправляются с запросами по умолчанию:

  • Sec-CH-UA перечисляет токены брендов с основными версиями, например "Chromium";v="142", "Google Chrome";v="142", "Not:A-Brand";v="99".
  • Sec-CH-UA-Mobile сообщает, мобильный ли это браузер, например ?0.
  • Sec-CH-UA-Platform называет операционную систему, например "Windows".

Подсказки с высокой энтропией отправляются только после того, как сервер запросит их заголовком ответа Accept-CH:

  • Sec-CH-UA-Full-Version-List содержит полные версии каждого бренда.
  • Sec-CH-UA-Platform-Version содержит версию операционной системы.
  • Sec-CH-UA-Arch и Sec-CH-UA-Bitness содержат архитектуру процессора и разрядность.
  • Sec-CH-UA-Model содержит модель устройства, что важно в основном для мобильных.

Поскольку подробные подсказки отправляются по запросу, расхождение в них может оставаться незаметным, пока сайт их не запросит. Конфигурация, которая выглядит верной при первой загрузке, может противоречить сама себе при второй. О том, как эти заголовки используются и где они расходятся, см. Client Hints Fingerprinting: HTTP-заголовки и идентичность, где значения заголовков сравниваются со значениями JavaScript в одной сессии.

Версия тех же сведений для JavaScript называется navigator.userAgentData. Ее свойства brands, mobile и platform повторяют заголовки с низкой энтропией. Подробные значения возвращает getHighEntropyValues(), который отдает промис:

navigator.userAgentData.brands;
navigator.userAgentData.mobile;
navigator.userAgentData.platform;
await navigator.userAgentData.getHighEntropyValues([
  'platformVersion',
  'architecture',
  'bitness',
  'fullVersionList',
  'model',
]);

Что бы ни говорили заголовки, эти свойства должны говорить то же самое. Спецификация определяет API и имена заголовков вместе, поэтому их и следует читать как одно описание.

Контекст Worker

У выделенного воркера, общего воркера и сервис-воркера есть собственный объект navigator. Воркер, который сообщает другой User-Agent или другой userAgentData, чем страница, запустившая его, является явной несогласованностью, ведь воркер принадлежит тому же браузеру. Любой подход, меняющий значения только на странице и не затрагивающий контекст воркера, оставляет этот разрыв открытым.

Записи GREASE и согласование версий

Chromium добавляет в список брендов намеренно бессмысленную запись GREASE, например "Not:A-Brand";v="99". Ее текст, версия и положение меняются от версии к версии браузера. Смысл в том, чтобы серверы не опирались на фиксированный формат списка, поэтому список, написанный вручную, в котором запись ошибочна или стоит не на своем месте, отличается от настоящего.

Номера версий требуют такой же аккуратности. Основная версия в строке User-Agent, версии в Sec-CH-UA и записи в fullVersionList должны описывать один и тот же выпуск Chromium. Полная версия 142.0.7444.60 в одном месте и основная версия 140 в другом считаются расхождением, даже если все названия брендов верны.

Совпадения, которые можно проверить

В совокупности убедительная идентичность удовлетворяет такому списку:

  1. Заголовок User-Agent совпадает с navigator.userAgent.
  2. Токены брендов в Sec-CH-UA совпадают с navigator.userAgentData.brands.
  3. Платформа в Sec-CH-UA-Platform совпадает с navigator.userAgentData.platform.
  4. Основная версия в строке User-Agent совпадает с версиями в токенах брендов Sec-CH-UA.
  5. Полные версии в Sec-CH-UA-Full-Version-List совпадают с fullVersionList из getHighEntropyValues().
  6. Версия платформы является реальным значением для заявленной операционной системы.
  7. Архитектура и разрядность подходят платформе, например x86 и 64 для настольного Windows.
  8. Каждое значение одинаково в основном потоке, в воркерах и в заголовках HTTP-запроса.

Почему подмена во фреймворке или через CDP оставляет разрывы

Фреймворки автоматизации позволяют легко изменить строку. Playwright принимает параметр userAgent при создании контекста, а Puppeteer предлагает page.setUserAgent():

const context = await browser.newContext({ userAgent: 'Custom UA String' });
await page.setUserAgent('Custom UA String');

Подмена, затрагивающая только строку, всегда меняет заголовок User-Agent и navigator.userAgent; остальное зависит от вызова. Измерено 2026-10-23 на Playwright 1.58.0 и Puppeteer 24.40.0:

  • browser.newContext({ userAgent }) в Playwright также вывел метаданные платформы: страница и Worker начинали сообщать новую платформу, а бренд и версии могли по-прежнему описывать базовый Chromium.
  • page.setUserAgent('Custom UA String') в Puppeteer оставил navigator.userAgentData с пустыми брендами и структурными значениями, а не с исходными.

Поэтому не исходите ни из того, что «исходная идентичность сохраняется», ни из того, что «вся идентичность следует за строкой». Прочитайте страницу, Worker и заголовки запроса в той сборке, которую используете; именно запись по четырём поверхностям выше показывает, какой это случай.

Chrome DevTools Protocol предлагает Network.setUserAgentOverride с необязательным объектом userAgentMetadata. Это полнее, потому что он может нести бренды, платформу и остальные подсказки. Но работа все равно перекладывается на вас. Список брендов, запись GREASE, полные версии и поля платформы приходится писать вручную и держать согласованными со строкой. Подмена действует на ту цель, которой была отправлена, поэтому каждой странице и каждому воркеру нужен отдельный вызов, а список, верный для одного выпуска Chromium, устаревает со следующим.

Установка дополнительных заголовков создает такую же проблему. Правило, переписывающее Sec-CH-UA на уровне сети, меняет запрос, но не то, что возвращает navigator.userAgentData внутри страницы. Идентичность тогда различается между сетевым слоем и слоем скриптов. О том, как задать дополнительные заголовки запроса в BotBrowser через флаг, профиль или CDP, рассказано в материале Пользовательские HTTP-заголовки в BotBrowser: флаг, профиль или CDP.

Это общие ограничения подходов, которые переписывают по одной поверхности. Правило заголовков, подмена строки и скрипт, правящий одно свойство, покрывают каждое свою поверхность, и ничто не гарантирует, что поверхности продолжат совпадать. Надежное решение состоит в том, чтобы выбрать идентичность один раз и сделать так, чтобы каждая поверхность выводилась из этого выбора.

Задание собственного User-Agent в BotBrowser

Профиль BotBrowser уже содержит согласованную основу. В нем хранятся согласованные между собой строка User-Agent, значения Client Hints и поля userAgentData, поэтому загрузка профиля дает согласованную идентичность без дополнительных флагов. Флаги ниже нужны, только когда требуется идентичность, отличающаяся от профиля, например идентичность Android на настольном компьютере.

Флаг --user-agent принимает исходную строку и работает вместе с набором флагов --bot-*, описывающих структурированную часть. Собственный User-Agent и полный контроль над userAgentData требуют лицензии ENT Tier3:

ФлагЧто задаетПример
--user-agentИсходную строку User-AgentMozilla/5.0 (Linux; ...)
--bot-platformПлатформу в userAgentDataWindows, Android
--bot-platform-versionВерсию операционной системы13, 10.0
--bot-modelМодель устройства, в основном для мобильныхSM-G991B
--bot-architectureАрхитектуру процессораx86, arm
--bot-bitnessРазрядность системы32, 64
--bot-mobileПризнак мобильного устройства в userAgentDatatrue, false

Значение --user-agent может содержать подстановки, которые заменяются при запуске браузера. {platform}, {platform-version}, {model}, {architecture} и {bitness} берут значения из соответствующих флагов --bot-*, а {ua-full-version}, {ua-major-version} и {brand-full-version} берут их из флагов версий. Подстановки удерживают строку и структурированные значения привязанными к одному источнику, поэтому нельзя изменить модель в одном месте и забыть о другом.

Версии задают два вспомогательных флага. --bot-ua-full-version (ENT Tier2) задает полную версию Chromium, а --bot-brand-full-version (ENT Tier2) задает версию самого бренда для таких брендов, как Edge. Документация рекомендует держать --bot-ua-full-version согласованным с основной версией Chromium вашего бинарного файла BotBrowser. О том, как выбирается и проверяется сам бренд в Chrome, Edge и Brave, см. Переключение бренда: Chrome, Edge, Brave, Opera, где описано, как задать бренд при запуске.

Идентичность Android на настольном компьютере выглядит так:

chrome --bot-profile="path/to/android-profile.enc" \
       --user-agent="Mozilla/5.0 (Linux; Android {platform-version}; {model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{ua-full-version} Mobile Safari/537.36" \
       --bot-platform=Android \
       --bot-platform-version=13 \
       --bot-model=SM-G991B \
       --bot-mobile=true \
       --bot-ua-full-version=142.0.7444.60

BotBrowser заменяет {platform-version}, {model} и {ua-full-version} значениями флагов, а затем формирует соответствующие navigator.userAgentData, бренды с корректной записью GREASE, значения высокой энтропии и заголовки запросов Client Hints. Согласно документации BotBrowser, результат остается согласованным в основном потоке, Workers и HTTP-запросах.

Идентичность настольного Windows строится по тому же образцу с другими значениями:

chrome --bot-profile="path/to/windows-profile.enc" \
       --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{ua-full-version} Safari/537.36" \
       --bot-platform=Windows \
       --bot-platform-version=10.0 \
       --bot-architecture=x86 \
       --bot-bitness=64 \
       --bot-mobile=false \
       --bot-ua-full-version=142.0.7444.60

Playwright и Puppeteer передают те же флаги через параметр args, а не через настройку userAgent фреймворка. Передавайте флаги через args, как это делают примеры в документации. О смене самого бренда, которая является отдельным элементом управления уровня ENT Tier2, читайте в статье о брендах, ссылка на которую приведена в конце.

Проверка идентичности на странице, в воркере и в запросе

После запуска результат можно проверить прямо в браузере без дополнительных инструментов. Цель состоит в том, чтобы прочитать одну и ту же идентичность в каждом из трех мест, где ее может прочитать сайт.

Начните со страницы. Откройте инструменты разработчика на любой странице и выполните в консоли такие строки:

navigator.userAgent;
navigator.userAgentData.brands;
navigator.userAgentData.platform;
await navigator.userAgentData.getHighEntropyValues([
  'platformVersion',
  'architecture',
  'bitness',
  'fullVersionList',
  'model',
]);

Затем проверьте запрос. Откройте панель «Сеть», выберите HTTPS-запрос верхнего уровня и прочитайте его заголовки. Заголовки User-Agent, Sec-CH-UA, Sec-CH-UA-Mobile и Sec-CH-UA-Platform должны совпадать с тем, что вывела консоль. Подробные заголовки, такие как Sec-CH-UA-Full-Version-List, появляются только после того, как сайт запросит их через Accept-CH, поэтому их отсутствие в первом запросе ожидаемо и не является расхождением.

Наконец, проверьте контекст воркера. Этот короткий фрагмент запускает выделенный воркер из blob и выводит то, что сообщает воркер:

const worker = new Worker(
  URL.createObjectURL(new Blob(['postMessage([navigator.userAgent, navigator.userAgentData.platform])']))
);
worker.onmessage = event => console.log(event.data);

Два значения, которые выводит воркер, должны совпадать со значениями страницы. Если ваши страницы используют сервис-воркер, откройте его на панели «Приложение» и выполните в его консоли те же два выражения.

Когда что-то выглядит неверно, симптом обычно указывает на причину:

СимптомВероятная причинаЧто изменить
Подстановка вроде {model} буквально остается в строкеНет соответствующего флага --bot-*Задайте --bot-model или уберите подстановку
Заголовок сообщает Android, а userAgentData.platform сообщает WindowsНе задан --bot-platformДобавьте --bot-platform=Android и остальные флаги платформы
Версии в Sec-CH-UA отличаются от строки User-AgentФлаг версии не соответствует основной версии ChromiumСогласуйте --bot-ua-full-version с версией Chromium бинарного файла
Строка изменилась, а подсказки за ней не последовалиСтрока была задана через параметр фреймворкаПередайте ее через --user-agent внутри args
Воркер сообщает значения, отличные от значений страницыСкрипт или подмена на странице изменили только страницуУберите подмену на странице и оставьте флаги браузера

Повторяйте проверку после каждого изменения профиля, версии Chromium или флагов идентичности, потому что каждое из них может сдвинуть одну поверхность, не сдвинув остальные. Успешная проверка показывает, что браузер внутренне согласован. Она не показывает, как конкретный сайт отнесется к сеансу, потому что сайты учитывают множество данных и со временем меняют собственные правила.

Выбор значений и планирование с учетом ограничений

Флаги делают поверхности согласованными друг с другом. Разумные значения они за вас не выбирают, поэтому качество результата зависит от того, что вы задаете:

  • Используйте реалистичные и актуальные версии. Версия браузера, которой почти никто уже не пользуется, необычна сама по себе, поэтому ориентируйтесь на основную версию Chromium вашего бинарного файла.
  • Выбирайте значения, которые подходят друг другу. Платформа, версия платформы, модель, архитектура и разрядность должны описывать один реальный тип устройства.
  • Согласуйте окружающие настройки. Идентичность, часовой пояс, локаль, язык и сетевой маршрут должны описывать один и тот же тип посетителя.
  • Сохраняйте одну идентичность на всю жизнь сеанса или учетной записи. Смена строки между визитами на один и тот же сайт создает непоследовательную историю.
  • Повторяйте проверку после обновлений браузера или профилей.

Эти флаги нужны не всегда. Загруженный профиль уже согласован, поэтому оставить --user-agent незаданным проще всего, чтобы сохранить согласованность. Обращайтесь к собственной идентичности, когда есть причина показать другую, например чтобы воспроизвести на настольном компьютере обращение, возникающее только на Android, а затем подтвердите каждую поверхность. Каждая дополнительная подмена добавляет значение, которое должно оставаться согласованным с остальными, поэтому меняйте только то, что требует задача.

Несколько практических вопросов возникают часто:

  • Что будет, если задать --user-agent без соответствующих флагов --bot-*? Задавайте соответствующий флаг --bot-* для каждой используемой подстановки; например, {model} требует --bot-model. Документация рекомендует использовать синтаксис подстановок вместе с соответствующими флагами.
  • Доходит ли идентичность до воркеров? Да. Согласно документации, значения остаются согласованными в основном потоке, Workers и HTTP-запросах, а проверка выше позволяет это подтвердить.
  • Нужна ли лицензия для настройки только с профилем? Пользовательские флаги не участвуют, используются значения, снятые в профиле. Полный контроль над userAgentData с собственным User-Agent требует лицензии ENT Tier3.
  • Меняет ли собственная идентичность набор возможностей браузера? Флаги меняют сообщаемые значения идентичности. Они не добавляют возможности, которых нет у базового браузера, поэтому после изменения проверьте важные для вас страницы.

BotBrowser поддерживает собственный шаблон --user-agent с подстановками вместе с --bot-platform, --bot-platform-version, --bot-model, --bot-architecture, --bot-bitness и --bot-mobile, и формирует согласованные бренды Client Hints, значения высокой энтропии и HTTP-заголовки, остающиеся единообразными в основном потоке, Workers и запросах. Это заменяет описанный выше ручной учет одной идентичностью, которую нужно проверить, вместо нескольких. BotBrowser не может заставить сайт принять выбранную идентичность, не заменяет выбор реалистичных и актуальных значений версий, а собственный User-Agent с полным контролем над userAgentData требует лицензии ENT Tier3.

О смене бренда браузера читайте в статье Несовпадение брендов браузера в Chrome, Edge и Brave. О том, как сайты используют Client Hints, читайте в статье Client Hints Fingerprinting: HTTP-заголовки и идентичность. О сочетании идентичности с географическими настройками читайте в статье Часовой пояс, локаль и язык.

Что добавлено по сравнению со справочной страницей

Справочная страница перечисляет параметр User-Agent и вспомогательные флаги. Она не даёт записи, которую можно сравнить между двумя запусками, а без неё расхождение читается неверно: переопределение на уровне фреймворка скрывает, какая поверхность действительно сместилась.

ПоверхностьГде читатьО чём говорит расхождение
СтраницаКонсоль страницыПереопределение сработало, но набор неполный
WorkerКонсоль WorkerИсправление на уровне страницы не дошло до Worker
ЗапросПанель «Сеть», запросПерезапись на уровне соединения, невидимая странице
HintsgetHighEntropyValues() на страницеПолитика разрешений или переопределение оставили набор высокой энтропии неполным

Сохраните все четыре чтения до того, как менять профиль. Выполните эти три выражения на странице, затем те же два в Worker, после чего откройте панель «Сеть» и скопируйте заголовки запроса в тот же файл. Держите по одному файлу на запуск, называя его по значению mode, и сравнивайте два файла, а не повторяйте проверку по памяти.

// страница и Worker принимают такую форму
const identity = {
  ua: navigator.userAgent,
  brands: navigator.userAgentData?.brands?.map(b => `${b.brand}/${b.version}`),
  mobile: navigator.userAgentData?.mobile,
  platform: navigator.userAgentData?.platform,
};
identity.high = await navigator.userAgentData.getHighEntropyValues([
  'platformVersion',
  'architecture',
  'bitness',
  'fullVersionList',
]);
console.log(JSON.stringify(identity));

Если запись страницы и запись Worker различаются, что-то ниже страницы переписывает идентичность. В фреймворке, который патчит только главный поток, это обычная причина; в той сборке, которую вы тестируете, подтвердите это, отключив переопределение и повторив оба чтения, потому что такой же разрыв могут давать и другие механизмы. Пока переопределение на месте, настройка флагов разрыв не закроет. Если заголовки запроса и запись страницы различаются, изменение произошло только на уровне соединения. Если high пуст, проверьте политику разрешений ch-ua-high-entropy-values на этой странице: getHighEntropyValues() возвращает значения только там, где политика их разрешает. Страница, которая никогда не запрашивала у сервера подробные подсказки, всё равно может их вернуть, а страница, отправившая Accept-CH, может отклонить вызов API, поэтому проверяйте API и заголовки запроса по отдельности, а не делайте вывод о сломанной конфигурации.

Источники

Практический итог

Если причины показывать другую идентичность нет, не задавайте --user-agent: загруженный профиль уже согласован. Когда она нужна, пишите шаблон вместе с соответствующими --bot-* и принимайте результат, только когда строка, подсказки, userAgentData и воркер совпали на одной основной версии Chromium.

#user agent#client hints#Identity#Ua-Ch#приватность

Эта статья входит в руководство: Поверхности идентичности браузера: обзор согласованности

Переведите BotBrowser из исследований в продакшн

Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.