12k
All articles

Можно ли по-прежнему доверять строке User-Agent?

Узнайте, каким данным User-Agent можно доверять, какие браузеры замораживают и как Client Hints Chromium и проверка функций заменяют ненадежный разбор строки.

OpenReplay Team
OpenReplay Team
Можно ли по-прежнему доверять строке User-Agent?

Отчасти. Строка User-Agent по-прежнему достоверно сообщает семейство браузера, мажорную версию, тип устройства (мобильное или десктопное) и семейство ОС. Но она не сообщает версию ОС, модель устройства, архитектуру процессора и (в браузерах на Chromium) минорную версию браузера.

Если вы нашли в логах Android 10; K от телефона, который явно работает не на Android 10, или Mac OS X 10_15_7 от MacBook на процессоре серии M, ничего не сломалось. Это значения-заглушки, и браузеры отправляют их намеренно.

В статье мы разберём актуальную строку Chrome по частям и покажем, какие поля заморожены в каждом движке. Затем рассмотрим, что заменяет эту строку в Chromium и когда её парсинг всё ещё оправдан.

Ключевые выводы

  • Все основные браузеры по-прежнему начинают строку User-Agent с Mozilla/5.0. Это токен совместимости, который ничего не говорит о браузере-отправителе.
  • Chrome сообщает Windows NT 10.0; Win64; x64, Macintosh; Intel Mac OS X 10_15_7, X11; Linux x86_64 или Linux; Android 10; K независимо от реального выпуска ОС, а минорная версия, номер сборки и патча у него всегда равны 0.0.0.
  • Начиная с Safari 26, Safari на iOS, iPadOS и visionOS сообщает замороженную версию ОС. Safari на Mac замораживает версию macOS с 2017 года.
  • User-Agent Client Hints существуют только в браузерах на базе Chromium. Firefox и Safari не отправляют заголовки Sec-CH-UA-*.
  • Любой клиент может отправить любой User-Agent, поэтому заголовок отсеивает только тех ботов, которые сами себя представляют.

Что на самом деле сообщает строка User-Agent в Chrome?

В актуальной строке UA десктопного Chrome от релиза к релизу меняется только один сегмент: мажорная версия. Всё остальное — фиксированные токены совместимости или замороженные значения платформы. Вот Chrome 154 на Windows:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/154.0.0.0 Safari/537.36
СегментЧто заявляетПравда ли это?
Mozilla/5.0Браузер, совместимый с MozillaБессмысленно: его отправляют все браузеры
(Windows NT 10.0; Win64; x64)Windows 10, 64-битная x86Заморожено: Windows 11 и машины на ARM отправляют то же значение
AppleWebKit/537.36 (KHTML, like Gecko)Движок WebKit, потомок KHTML, похожий на GeckoНаследие совместимости: движок Chrome — Blink
Chrome/154.0.0.0Chrome 154.0.0.0Мажорная версия реальная, 0.0.0 — заглушка
Safari/537.36SafariЭто не Safari. Токен оставлен, чтобы код, ищущий «Safari», продолжал срабатывать

Справочник MDN по Firefox описывает Mozilla/5.0 как общий токен, заявляющий о совместимости с Mozilla. Его отправляет почти каждый браузер, какой бы движок в нём ни работал. Справочник MDN по User-Agent подтверждает, что браузеры на Blink содержат KHTML, like Gecko и Safari исключительно как токены совместимости. В строке Chrome AppleWebKit/537.36 (KHTML, like Gecko) и Safari/537.36 не описывают ни движок Chrome, ни Safari. Это фиксированные токены, сохранённые для того, чтобы старый код сниффинга продолжал находить совпадения. Чтобы получить такой же разбор своей строки, вставьте её в парсер user agent от OpenReplay.

Какие поля User-Agent заморожены, а какие по-прежнему достоверны?

Все три движка заморозили высокоэнтропийные части строки: версию ОС, модель устройства и архитектуру процессора. Chromium вдобавок обнуляет минорную версию браузера. Низкоэнтропийные части по-прежнему отражают реальный браузер. План User-Agent Reduction в Chrome начал фиксировать минорную версию, номер сборки и патча как 0.0.0 в Chrome 101 (2022). Deprecation trial, позволявший сайтам сохранять полную строку, завершился 23 сентября 2023 года, и с тех пор каждая загрузка страницы получает сокращённую строку. Руководство MDN по сокращению UA перечисляет фиксированные значения платформ, включая Android 10; K на Android.

Chrome / EdgeFirefoxSafari
Семейство браузера, мажорная версияРеальныеРеальныеРеальные (Version/)
Мобильное/десктоп, семейство ОСРеальныеРеальныеРеальные
Версия ОСЗамороженаОграничена сверху (macOS 10.15, Android 10)Заморожена (macOS; iOS начиная с 26)
Модель устройстваK на AndroidНе отправляетсяНе отправляется
Архитектура процессораЗамороженаЗамороженаMac всегда «Intel»
Минорная версия0.0.0Не раскрываетсяРеальная (Version/)
UA Client HintsДаНетНет

Edge использует те же замороженные токены, что и Chrome, и добавляет Edg/. Начиная с Firefox 87, Firefox сообщает все выпуски macOS от Big Sur и новее как 10.15 и помечает Mac на Apple Silicon как Intel. Начиная с Firefox для Android 122, он сообщает Android 10 независимо от реальной версии. Согласно публикации WebKit о выпуске Safari 26.0, Safari на Mac отправляет одно и то же значение Intel Mac OS X 10_15_7 с 2017 года. Начиная с Safari 26 (сентябрь 2025), Safari на iOS, iPadOS и visionOS также сообщает замороженную версию iOS 18 вместо реальной. Safari 26.0 отправлял 18_6. Начиная с Safari 26.2, WebKit привязывает значение к последнему выпуску iOS 18, поэтому актуальные строки содержат 18_7. Токен Version/ продолжает обновляться с каждым выпуском.

Сокращение UA в Chromium распространяется на Chrome для Windows, macOS, Linux, ChromeOS и Android. Оно не затрагивает Android WebView и Chrome для iOS. На практике это означает следующее:

  • Windows 10 и Windows 11 выглядят одинаково.
  • Mac на Apple Silicon сообщают «Intel» во всех трёх движках.
  • Строка Chrome для Android с Android 10; K не описывает устройство на Android 10. Любой Chrome на Android отправляет эту версию и модель-заглушку K.

Почему определение возможностей лучше определения браузера?

Название браузера не даёт надёжной информации о том, что он умеет. Feature detection (определение возможностей) проверяет возможность напрямую, и это было верно ещё до того, как какую-либо строку заморозили. Проверка по UA ломается, когда строка лжёт, когда новый браузер начинает поддерживать API или когда в старой версии его нет. Baseline даёт кросс-браузерную картину того, когда на функцию можно безопасно полагаться. Если пока нельзя, пробел закрывает проверка во время выполнения:

// Brittle: guesses capability from a name
if (/Chrome\/\d+/.test(navigator.userAgent)) enableShareButton();

// Direct: asks the browser
if ('share' in navigator) enableShareButton();

Как работают User-Agent Client Hints?

User-Agent Client Hints — это набор заголовков запроса Sec-CH-UA-*, которые браузеры на базе Chromium отправляют вместо сведений, которых больше нет в строке UA. Chrome и Edge по умолчанию отправляют Sec-CH-UA, Sec-CH-UA-Mobile и Sec-CH-UA-Platform. Firefox и Safari не отправляют ни один из них. MDN относит Sec-CH-UA-Platform к низкоэнтропийным подсказкам, поэтому браузер включает его без запроса со стороны сервера, если только это не блокирует политика разрешений (permissions policy). Все остальные подсказки нужно запрашивать.

Чтобы запросить подсказки, сервер перечисляет их в Accept-CH, и браузер включает их в последующие защищённые запросы к этому источнику (origin).

На первый запрос Accept-CH никак не влияет. Чтобы получить высокоэнтропийную подсказку уже в самом первом запросе, сервер указывает её в Critical-CH, а также в Accept-CH. Вместо отрисовки первого ответа браузер повторяет запрос, на этот раз с подсказкой. Серверу также следует добавить подсказку в Vary, чтобы кеши хранили каждый вариант отдельно.

import https from 'node:https';
import { readFileSync } from 'node:fs';

https.createServer(
  { key: readFileSync('key.pem'), cert: readFileSync('cert.pem') },
  (req, res) => {
    const platform = req.headers['sec-ch-ua-platform'];           // default hint
    const version = req.headers['sec-ch-ua-platform-version'];    // opt-in only

    res.setHeader('Accept-CH', 'Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch');
    res.setHeader('Critical-CH', 'Sec-CH-UA-Platform-Version');
    res.setHeader('Vary', 'Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch');
    res.setHeader('Content-Type', 'text/plain');
    res.end(`platform=${platform ?? 'n/a'} version=${version ?? 'n/a'}\n`);
  }
).listen(8443);

Node приводит имена заголовков к нижнему регистру. Значения приходят в виде строк structured fields в кавычках, например "Windows". В браузере navigator.userAgentData.getHighEntropyValues() возвращает те же данные без какой-либо настройки заголовков. MDN помечает uaFullVersion как устаревшее в пользу fullVersionList.

async function describeClient() {
  const uaData = navigator.userAgentData;
  if (!uaData) return { source: 'ua', ua: navigator.userAgent }; // Firefox, Safari
  const high = await uaData.getHighEntropyValues([
    'platformVersion', 'architecture', 'model', 'fullVersionList',
  ]);
  return { source: 'ua-ch', brands: uaData.brands, mobile: uaData.mobile, ...high };
}

Когда парсинг строки User-Agent всё ещё оправдан?

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

Сегменты аналитики. Парсинг user agent точен для сегментов аналитики вида «Chrome 154, десктоп, Windows». Для сегментов «Windows 10» или «Android 10» это не так: они незаметно поглощают более новые выпуски, поэтому рассматривайте их только как семейство ОС. Проверяйте Edg/ раньше Chrome/, а Chrome/ раньше Safari/, потому что каждая строка содержит токены, идущие после неё в этом списке.

Фильтрация ботов. RFC 9110 определяет User-Agent как заголовок, который предоставляет клиент, и никто его не проверяет. Любой клиент может отправить любое значение. Заголовок позволяет отловить краулеры, которые себя идентифицируют, и бесполезен против ботов, которые этого не делают.

Обращения в поддержку. Когда пользователь сообщает об ошибке, обычно достаточно примерно знать, чем он пользовался, а не точную сборку. Инструменты session replay (воспроизведения сессий) записывают UA вместе с каждой сессией, и этого хватает, чтобы увидеть, что ошибка воспроизводится в Firefox на macOS, но не в Chrome. Если записывать рядом результаты проверок возможностей, круг поиска сузится ещё больше.

Заключение

Строке User-Agent можно доверять в части семейства браузера, мажорной версии, типа устройства (мобильное или десктопное) и семейства ОС. Всё остальное в ней считайте заглушкой. Чтобы применить эти выводы на практике, проверьте код на наличие ветвлений, которые считывают из строки версию ОС, модель устройства или минорную версию. Замените проверки возможностей на основе UA на feature detection, а там, где действительно нужны подробности о платформе, переходите на Client Hints с фолбэком для Firefox и Safari, которые их не отправляют.

Часто задаваемые вопросы

Как отличить Windows 11 от Windows 10, если user agent сообщает Windows NT 10.0?

Запросите клиентскую подсказку platformVersion: либо через navigator.userAgentData.getHighEntropyValues(['platformVersion']), либо отправив Accept-CH: Sec-CH-UA-Platform-Version. Согласно документации Microsoft, значения от 1.0.0 до 10.0.0 соответствуют Windows 10, а 13.0.0 и выше — Windows 11; в примере кода Microsoft мажорная версия 13 и выше считается Windows 11. Firefox не отправляет Client Hints, поэтому в нём эти две системы не различить.

Как долго браузер продолжает отправлять подсказки, запрошенные через Accept-CH?

Chrome сохраняет настройки Accept-CH для каждого источника на диск, и начиная с Chrome 103 у них нет фиксированного срока действия. Они действуют, пока пользователь не очистит cookie или данные сайта для этого источника, а также удаляются вместе с сессионными cookie. Сервер может повторно отправить Accept-CH, чтобы заменить список, отправить пустой Accept-CH, чтобы отключить все подсказки, или отправить Clear-Site-Data: 'clientHints'.

Почему заголовок Sec-CH-UA содержит бренд вроде Not A;Brand?

Это намеренно фиктивная запись, известная как GREASE. Chromium добавляет заведомо неверный бренд с низким номером версии и меняет его пунктуацию и позицию в списке. Это вынуждает серверы корректно парсить заголовок, а не сопоставлять его с фиксированной строкой или фиксированным списком брендов. Разбирайте Sec-CH-UA как список structured fields, ищите нужный бренд, например Chromium, Google Chrome или Microsoft Edge, и игнорируйте незнакомые записи.

Почему мой парсер user agent определяет Chrome на iPhone как Safari?

Chrome для iOS отправляет строку user agent Mobile Safari с токеном CriOS/ вместо Version/, поэтому токена Chrome/ в строке нет. Firefox для iOS поступает так же с токеном FxiOS/. Парсер, который ищет только Chrome/ и Firefox/, в итоге определяет браузер как Safari, поэтому сначала проверяйте CriOS/ и FxiOS/. Сокращение UA в Chromium не распространяется на Chrome для iOS.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.