12k
All articles

Peut-on encore se fier à la chaîne User-Agent ?

Découvrez quels champs du user-agent restent fiables, lesquels les navigateurs figent et comment les Client Hints de Chromium et la détection de fonctionnalités évitent les erreurs.

OpenReplay Team
OpenReplay Team
Peut-on encore se fier à la chaîne User-Agent ?

En partie. La chaîne User-Agent indique toujours de manière fiable la famille du navigateur, sa version majeure, le type d’appareil (mobile ou ordinateur) et la famille du système d’exploitation. En revanche, elle ne renseigne plus correctement la version de l’OS, le modèle de l’appareil, l’architecture du processeur ni, dans les navigateurs Chromium, la version mineure du navigateur.

Vous avez peut-être trouvé Android 10; K dans vos logs pour un téléphone qui n’exécute manifestement pas Android 10, ou Mac OS X 10_15_7 pour un MacBook à puce M. Rien n’est cassé : ces valeurs sont des valeurs de substitution (placeholders), et les navigateurs les envoient délibérément.

Cet article décortique une chaîne Chrome actuelle et montre quels champs sont figés dans chaque moteur. Il présente ensuite ce qui remplace la chaîne dans Chromium, ainsi que les cas où son analyse reste un choix pertinent.

Points clés

  • Tous les principaux navigateurs commencent encore leur chaîne User-Agent par Mozilla/5.0. Ce jeton de compatibilité ne dit rien du navigateur qui l’envoie.
  • Chrome indique Windows NT 10.0; Win64; x64, Macintosh; Intel Mac OS X 10_15_7, X11; Linux x86_64 ou Linux; Android 10; K, quelle que soit la version réelle de l’OS. Ses numéros de version mineure, de build et de correctif valent toujours 0.0.0.
  • Depuis Safari 26, Safari sur iOS, iPadOS et visionOS indique une version d’OS figée. Safari sur Mac fige la version de macOS depuis 2017.
  • Les User-Agent Client Hints n’existent que dans les navigateurs basés sur Chromium. Firefox et Safari n’envoient aucun en-tête Sec-CH-UA-*.
  • N’importe quel client peut envoyer n’importe quel User-Agent. L’en-tête ne permet donc de filtrer que les bots qui s’annoncent comme tels.

Que contient réellement une chaîne User-Agent de Chrome ?

Dans la chaîne UA actuelle de Chrome pour ordinateur, un seul segment change d’une version à l’autre : la version majeure. Tout le reste est soit un jeton de compatibilité fixe, soit une valeur de plateforme figée. Voici Chrome 154 sous Windows :

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/154.0.0.0 Safari/537.36
SegmentCe qu’il affirmeEst-ce vrai ?
Mozilla/5.0Un navigateur compatible MozillaSans signification. Tous les navigateurs l’envoient
(Windows NT 10.0; Win64; x64)Windows 10, x86 64 bitsFigé. Windows 11 et les machines ARM envoient la même valeur
AppleWebKit/537.36 (KHTML, like Gecko)Un moteur WebKit dérivé de KHTML et semblable à GeckoVestige de compatibilité. Le moteur de Chrome est Blink
Chrome/154.0.0.0Chrome 154.0.0.0La version majeure est réelle. 0.0.0 est une valeur de substitution
Safari/537.36SafariCe n’est pas Safari. Le jeton est conservé pour que le code qui recherche « Safari » continue de fonctionner

La documentation MDN consacrée à Firefox décrit Mozilla/5.0 comme un jeton générique qui revendique une compatibilité avec Mozilla. Presque tous les navigateurs l’envoient, quel que soit leur moteur. La référence MDN sur l’en-tête User-Agent confirme que les navigateurs basés sur Blink incluent KHTML, like Gecko et Safari uniquement comme jetons de compatibilité.

Dans une chaîne Chrome, AppleWebKit/537.36 (KHTML, like Gecko) et Safari/537.36 ne décrivent donc ni le moteur de Chrome ni Safari. Ce sont des jetons fixes, conservés pour que l’ancien code de détection (sniffing) continue de fonctionner. Pour obtenir la même analyse de votre propre chaîne, collez-la dans le parseur de User-Agent d’OpenReplay.

Quels champs du User-Agent sont figés, et lesquels restent fiables ?

Les trois moteurs ont figé les parties à forte entropie de la chaîne : version de l’OS, modèle de l’appareil et architecture du processeur. Chromium remet également à zéro la version mineure du navigateur. Les parties à faible entropie, elles, reflètent toujours le navigateur réel.

Le plan de réduction du User-Agent de Chrome a commencé à fixer les numéros de version mineure, de build et de correctif à 0.0.0 avec Chrome 101 (2022). Un essai d’obsolescence (deprecation trial) permettait aux sites de conserver la chaîne complète. Depuis sa fin, le 23 septembre 2023, chaque chargement de page reçoit la chaîne réduite. Le guide MDN sur la réduction du UA répertorie les valeurs de plateforme fixes, dont Android 10; K sur Android.

Chrome / EdgeFirefoxSafari
Famille du navigateur, version majeureValeur réelleValeur réelleValeur réelle (Version/)
Mobile ou ordinateur, famille d’OSValeur réelleValeur réelleValeur réelle
Version de l’OSFigéePlafonnée (macOS 10.15, Android 10)Figée (macOS ; iOS depuis la version 26)
Modèle de l’appareilK sur AndroidJamais envoyéJamais envoyé
Architecture du processeurFigéeFigéeToujours « Intel » sur Mac
Version mineure0.0.0Non exposéeValeur réelle (Version/)
UA Client HintsOuiNonNon

Edge utilise les mêmes jetons figés que Chrome et y ajoute Edg/.

Depuis Firefox 87, Firefox présente toutes les versions de macOS à partir de Big Sur comme la 10.15 et identifie les Mac Apple Silicon comme des Mac Intel. Depuis Firefox pour Android 122, il indique Android 10, quelle que soit la version réelle.

D’après l’article de WebKit consacré à Safari 26.0, Safari sur Mac envoie la même valeur Intel Mac OS X 10_15_7 depuis 2017. Depuis Safari 26 (septembre 2025), Safari sur iOS, iPadOS et visionOS indique lui aussi une version d’iOS 18 figée au lieu de la version réelle. Safari 26.0 envoyait 18_6. À partir de Safari 26.2, WebKit fixe la valeur sur la dernière version d’iOS 18 : les chaînes actuelles envoient donc 18_7. Le jeton Version/, lui, continue d’être mis à jour à chaque version.

La réduction de Chromium s’applique à Chrome sous Windows, macOS, Linux, ChromeOS et Android. Elle ne concerne ni Android WebView ni Chrome pour iOS. En pratique :

  • Windows 10 et Windows 11 sont indiscernables.
  • Les Mac Apple Silicon sont identifiés comme « Intel » dans les trois moteurs.
  • Une chaîne Chrome pour Android contenant Android 10; K ne décrit pas un appareil sous Android 10. Toutes les instances de Chrome sur Android envoient cette version et le modèle de substitution K.

Pourquoi la détection de fonctionnalités est-elle préférable à la détection du navigateur ?

Le nom d’un navigateur ne dit rien de fiable sur ses capacités. La détection de fonctionnalités (feature detection) vérifie directement la capacité recherchée, et c’était déjà vrai bien avant que les chaînes ne soient figées.

Une vérification basée sur le UA échoue dans plusieurs cas : lorsque la chaîne ment, lorsqu’un nouveau navigateur implémente l’API, ou lorsqu’une ancienne version ne la prend pas en charge. Baseline vous offre une vue multinavigateur du moment où l’on peut s’appuyer sans risque sur une fonctionnalité. Lorsque ce n’est pas encore le cas, une vérification à l’exécution comble le manque :

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

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

Dans cet exemple, la première approche est fragile, car elle déduit une capacité à partir d’un nom. La seconde interroge directement le navigateur.

Comment fonctionnent les User-Agent Client Hints ?

Les User-Agent Client Hints sont un ensemble d’en-têtes de requête Sec-CH-UA-*. Les navigateurs basés sur Chromium les envoient pour transmettre les informations que la chaîne UA ne contient plus.

Chrome et Edge envoient Sec-CH-UA, Sec-CH-UA-Mobile et Sec-CH-UA-Platform par défaut. Firefox et Safari n’en envoient aucun. MDN classe Sec-CH-UA-Platform parmi les hints à faible entropie. Le navigateur l’inclut donc sans que le serveur ait à le demander, sauf si une permissions policy le bloque. Tous les autres hints doivent être demandés explicitement.

Pour demander des hints, le serveur les liste dans l’en-tête Accept-CH. Le navigateur les inclut ensuite dans les requêtes sécurisées suivantes vers cette origine.

Accept-CH n’a aucun effet sur la toute première requête. Pour obtenir un hint à forte entropie dès cette première requête, le serveur doit le mentionner à la fois dans Critical-CH et dans Accept-CH. Au lieu d’afficher cette première réponse, le navigateur renvoie alors la requête, cette fois avec le hint. Le serveur doit également ajouter le hint à l’en-tête Vary, afin que les caches stockent chaque variante séparément.

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 convertit les noms d’en-têtes en minuscules. Les valeurs arrivent sous forme de chaînes structured field entre guillemets, par exemple "Windows".

Côté navigateur, navigator.userAgentData.getHighEntropyValues() renvoie les mêmes données, sans aucune configuration d’en-têtes. MDN indique que uaFullVersion est obsolète et doit être remplacé par 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 };
}

Dans quels cas l’analyse de la chaîne User-Agent reste-t-elle pertinente ?

L’analyse de la chaîne reste pertinente dans deux conditions : vous n’avez besoin que des champs fiables, et une erreur ne vous coûte pas grand-chose.

Segments d’analytics. L’analyse du User-Agent est fiable pour des segments comme « Chrome 154, ordinateur, Windows ». Un segment « Windows 10 » ou « Android 10 » ne l’est pas : il absorbe silencieusement les versions plus récentes. Considérez-le uniquement comme une indication de la famille d’OS.

Respectez également un ordre de vérification précis : recherchez Edg/ avant Chrome/, et Chrome/ avant Safari/. En effet, chaque chaîne contient les jetons qui la suivent dans cette liste.

Filtrage des bots. La RFC 9110 définit le User-Agent comme un en-tête fourni par le client, que rien ne vérifie. N’importe quel client peut donc envoyer n’importe quelle valeur. L’en-tête permet d’intercepter les robots d’indexation qui s’identifient, mais reste inefficace contre les bots qui ne le font pas.

Tickets de support. Lorsqu’un utilisateur signale un bug, il suffit généralement de savoir à peu près ce qu’il utilisait, sans connaître son build exact. Les outils de session replay enregistrent le UA avec chaque session. C’est suffisant pour constater, par exemple, qu’un bug se reproduit dans Firefox sous macOS, mais pas dans Chrome. Enregistrer en parallèle le résultat de vos détections de fonctionnalités permet d’affiner encore le diagnostic.

Conclusion

Vous pouvez vous fier à la chaîne User-Agent pour la famille du navigateur, sa version majeure, le type d’appareil (mobile ou ordinateur) et la famille de l’OS. Considérez tout le reste comme des valeurs de substitution.

Pour mettre ces conclusions en pratique, procédez en trois étapes :

  1. Auditez votre code à la recherche de toute branche qui lit une version d’OS, un modèle d’appareil ou une version mineure dans la chaîne.
  2. Remplacez les vérifications de capacités basées sur le UA par de la détection de fonctionnalités.
  3. Pour les besoins réels en informations de plateforme, passez aux Client Hints, avec une solution de repli pour Firefox et Safari, qui ne les envoient pas.

FAQ

Comment distinguer Windows 11 de Windows 10 si le User-Agent indique Windows NT 10.0 ?

Demandez le client hint platformVersion, soit avec navigator.userAgentData.getHighEntropyValues(['platformVersion']), soit en envoyant Accept-CH: Sec-CH-UA-Platform-Version. Selon la documentation de Microsoft, les valeurs de 1.0.0 à 10.0.0 correspondent à Windows 10, et les valeurs à partir de 13.0.0 à Windows 11. Son exemple de code considère qu'une version majeure supérieure ou égale à 13 correspond à Windows 11. Firefox n'envoie aucun Client Hint : il est donc impossible de distinguer les deux systèmes avec ce navigateur.

Combien de temps un navigateur continue-t-il d'envoyer les hints demandés via Accept-CH ?

Chrome enregistre sur le disque les préférences Accept-CH de chaque origine. Depuis Chrome 103, elles n'ont plus de date d'expiration fixe : elles sont conservées jusqu'à ce que l'utilisateur efface les cookies ou les données de ce site. Elles sont également supprimées en même temps que les cookies de session. Le serveur peut renvoyer Accept-CH pour remplacer la liste, envoyer un Accept-CH vide pour désactiver tous les hints, ou envoyer Clear-Site-Data: 'clientHints'.

Pourquoi l'en-tête Sec-CH-UA contient-il une marque comme Not A;Brand ?

Il s'agit d'une entrée volontairement factice, appelée GREASE. Chromium ajoute une marque délibérément erronée, associée à un numéro de version faible, et fait varier sa ponctuation ainsi que sa position dans la liste. Cette technique oblige les serveurs à analyser correctement l'en-tête, au lieu de rechercher une chaîne fixe ou une liste de marques figée. Analysez Sec-CH-UA comme une liste structured field, recherchez la marque qui vous intéresse (par exemple Chromium, Google Chrome ou Microsoft Edge) et ignorez toute entrée que vous ne reconnaissez pas.

Pourquoi mon parseur de User-Agent identifie-t-il Chrome sur iPhone comme Safari ?

Chrome pour iOS envoie la chaîne User-Agent de Mobile Safari, avec un jeton CriOS/ à la place de Version/. La chaîne ne contient donc aucun jeton Chrome/. Firefox pour iOS fait de même avec FxiOS/. Un parseur qui ne recherche que Chrome/ et Firefox/ se rabat alors sur Safari : vérifiez donc d'abord la présence de CriOS/ et FxiOS/. Notez que la réduction du UA de Chromium ne s'applique pas à Chrome pour 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.