User-Agent Client Hints - теперь у вас три сигнала, которые обязаны совпадать
Браузеры перестали класть большую часть полезных подробностей о платформе прямо в заголовок User-Agent. Эти подробности не исчезли, они переехали в отдельный механизм под названием User-Agent Client Hints, разделённый между набором заголовков запроса и JavaScript-API. Для тех, кто строит или оценивает автоматизацию браузера, практическое следствие не в том, что снимать отпечатки стало труднее. Оно в том, что теперь есть три места, где браузер описывает собственную платформу, и все три обязаны говорить одно и то же.
Два уровня подсказок
Client Hints разделены на низкоэнтропийный и высокоэнтропийный уровни, и разделение существует потому, что высокоэнтропийный раскрывает достаточно, чтобы сузить круг до конкретного устройства, поэтому браузер выдаёт его только по явному запросу.
Низкоэнтропийные подсказки прикрепляются к запросам автоматически, без всякого согласия:
Sec-CH-UA- бренд браузера и значимая версия, отправляемые списком (браузеры намеренно сообщают несколько записей брендов, включая заведомо бессмысленные, поэтому сам по себе список не является точным отпечатком версии)Sec-CH-UA-Mobile- работает ли браузер в мобильном контекстеSec-CH-UA-Platform- семейство ОС, например"Windows"или"macOS"
Высокоэнтропийные подсказки отправляются, только если сервер их запросил заголовком ответа Accept-CH (или эквивалентным метатегом), после чего браузер включает их в последующие запросы к этому источнику. Сюда входят полная версия платформы, архитектура процессора, разрядность, модель устройства и полный список брендов и версий вместо сокращённого. Те же данные доступны JavaScript страницы через navigator.userAgentData.getHighEntropyValues(), который возвращает промис, чтобы браузер мог ограничивать или притормаживать доступ к ним.
Три места, которые обязаны совпадать
Вот часть, важная для всего, что представляется конкретным браузером и платформой. Одна сессия браузера теперь заявляет свою личность по трём независимым каналам:
- Сама строка
User-Agent- замороженная и урезанная, но всё ещё присутствующая и всё ещё проверяемая серверами, которые не перешли на Client Hints. navigator.userAgentData- читается JavaScript страницы, включая любой скрипт детекта, который крутит сайт.- Заголовки запроса
Sec-CH-UA-*- читаются на стороне сервера, независимо от того, что сообщает JavaScript страницы.
Настоящий немодифицированный браузер порождает все три из одного внутреннего состояния, поэтому они всегда согласованы. Подход к подмене, который правит только одно из них, чаще всего слой JavaScript как самый доступный для инъекции скрипта, оставляет два других показывать настоящие значения браузера. Сайт, сверяющий navigator.userAgentData.platform с полученным заголовком Sec-CH-UA-Platform или с подстрокой, всё ещё присутствующей в User-Agent, ловит несовпадение мгновенно. Это несовпадение - сигнал сильнее, чем был бы обычный немодифицированный отпечаток, потому что настоящее устройство никогда не противоречит само себе в том, что оно такое. Залатать один слой и забыть про два других - именно так попытка подмены изготавливает ровно то противоречие, которое ищет скрипт детекта.
Выгрузите все три сами
Выполните это в консоли браузера, чтобы увидеть, что сообщает JavaScript:
(async () => {
console.log('User-Agent string:', navigator.userAgent)
console.log('userAgentData (low-entropy):', navigator.userAgentData)
const high = await navigator.userAgentData.getHighEntropyValues([
'platform', 'platformVersion', 'architecture', 'bitness', 'model', 'fullVersionList'
])
console.log('userAgentData (high-entropy):', high)
})()
И на сервере, чтобы увидеть, что реально несли заголовки запроса:
// минимальный обработчик Node http или любой маршрут Express/Astro
export function logClientHints(req) {
console.log('User-Agent:', req.headers['user-agent'])
console.log('Sec-CH-UA:', req.headers['sec-ch-ua'])
console.log('Sec-CH-UA-Mobile:', req.headers['sec-ch-ua-mobile'])
console.log('Sec-CH-UA-Platform:', req.headers['sec-ch-ua-platform'])
}
Сравните платформу и версию во всех трёх выводах. В обычном браузере они сходятся по построению. В автоматизированной схеме это согласие нужно проверять, а не предполагать.
Когда всё это можно не трогать
Если ваш целевой сайт читает только обычный заголовок User-Agent и никогда не выставляет Accept-CH и не запрашивает navigator.userAgentData в своих скриптах, ничего из вышеописанного не участвует: проверяется только замороженная строка, и согласовывать больше нечего. Большинство простого скрапинга и малообъёмной автоматизации вообще не встречает сайтов, запрашивающих высокоэнтропийные подсказки, а строить логику согласованности для сигнала, который цель никогда не читает, - потраченный впустую труд. Проверьте, что цель реально запрашивает, прежде чем считать, что вам нужно управлять всеми тремя.
Поскольку AntiBrow применяет отпечаток в самом движке браузера, а не через инъекцию скрипта, строка User-Agent, navigator.userAgentData и заголовки Sec-CH-UA-* порождаются из одной личности и остаются согласованными по построению, в том числе после перезапуска. Общая проверка согласованности, охватывающая не только этот сигнал, - в чек-листе согласованности отпечатка. Что такое отпечаток и почему его вообще оценивают - в статье что такое отпечаток браузера.
Попробуйте на бесплатном тарифе.
Неограниченное число локальных профилей, без карты. Проверьте результат детекторами сами.