Anti-Bot

What Is User-Agent Client Hints Fingerprinting?

What Is User-Agent Client Hints Fingerprinting? — conceptual illustration
On this page

User-Agent Client Hints (UA-CH) are a set of structured HTTP headers plus a matching JavaScript API that report the same browser and operating-system facts the old User-Agent text string used to carry. On every request Chrome sends Sec-CH-UA, Sec-CH-UA-Platform, and Sec-CH-UA-Mobile; it exposes the same data to JavaScript through navigator.userAgentData; and it answers requests for more detailed "high-entropy" hints (Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Model) - "entropy" here meaning how much a value narrows down who you are. Anti-bot systems fingerprint scrapers by checking whether all three sources - the legacy UA string, the Sec-CH-UA headers, and the JS API - tell exactly the same story.

Quick facts

HeadersSec-CH-UA, Sec-CH-UA-Platform, Sec-CH-UA-Mobile (low-entropy, always sent)
On requestSec-CH-UA-Arch, -Bitness, -Model, -Platform-Version, -Full-Version-List
JS mirrornavigator.userAgentData.getHighEntropyValues()
Core checkUA string == Sec-CH-UA headers == userAgentData (all must agree)
GREASESec-CH-UA includes a deliberately random brand entry browsers must tolerate

The three sources that must agree

Since Chrome 89+ the browser reports its identity in three places, all generated from one internal source - so on a real browser they always match:

  • The UA string - navigator.userAgent and the User-Agent header, now frozen/reduced on Chrome (deliberately trimmed and locked down).
  • Sec-CH-UA headers - Sec-CH-UA: "Chromium";v="131", "Not_A Brand";v="24", "Google Chrome";v="131" plus platform and mobile flags on every request.
  • navigator.userAgentData - the JS API, including getHighEntropyValues(["platform","platformVersion","architecture","model","fullVersionList"]).

A scraper that edits the User-Agent header but leaves Sec-CH-UA and navigator.userAgentData at their real (or missing) values is instantly incoherent - the three sources contradict each other. Python HTTP clients that send a Chrome UA string but no Sec-CH-UA headers at all are an obvious tell, because real Chrome never omits them.

High-entropy hints and the GREASE trap

Low-entropy hints (brand, mobile, platform) ship on every request. High-entropy hints (full version list, architecture, bitness, model, platform version) are sent only when the server asks for them via the Accept-CH response header - so an anti-bot endpoint can request them and watch how the client answers. The values must agree with each other: Sec-CH-UA-Mobile: ?1 (claiming a mobile device) paired with a desktop platform, or Sec-CH-UA-Arch: "arm" with Sec-CH-UA-Bitness: "32" on a claimed Apple Silicon Mac, are contradictions.

The Sec-CH-UA header also contains a GREASE entry - a deliberately fake brand like "Not_A Brand";v="24" that Chrome adds so servers cannot hardcode the brand list, and whose exact text and punctuation vary by Chrome version. Vendors know the real GREASE patterns per version; a hand-built header with the wrong GREASE string, or with the brands in the wrong order, fails the check. navigator.userAgentData.brands must contain the same GREASE entry as the header.

Why this is hard to spoof by hand

Getting UA-CH right means generating one complete, version-accurate identity across all three surfaces at once: the reduced UA string, every Sec-CH-UA header with the correct GREASE and ordering, and a navigator.userAgentData object whose getHighEntropyValues() returns matching platform/arch/model. Change the Chrome major version and all of them have to move together.

This is why brand-switching and UA spoofing are gated behind engine-level tooling in serious anti-detect browsers - the engine regenerates all three from one config so they cannot drift apart. A managed scraping API solves it the same way: it impersonates a real Chrome build end to end rather than editing one header. Patching just the UA string with a Python requests override is the single most common reason a scraper that "looks like Chrome" still gets blocked.

Code example

javascript
// Server-side coherence check an anti-bot endpoint runs
// 1) ask for high-entropy hints
res.setHeader('Accept-CH',
  'Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Model');

// 2) on the next request, compare all sources
function coherent(req) {
  const ua   = req.headers['user-agent'] || '';
  const chUA = req.headers['sec-ch-ua'] || '';        // '"Chromium";v="131", "Not_A Brand";v="24"...'
  const plat = req.headers['sec-ch-ua-platform'] || ''; // '"Windows"'
  const mob  = req.headers['sec-ch-ua-mobile'] || '';   // '?0'

  if (ua.includes('Chrome') && !chUA) return false;     // Chrome never omits Sec-CH-UA
  if (ua.includes('Windows') && !plat.includes('Windows')) return false;
  if (mob === '?1' && plat.includes('Windows')) return false; // mobile flag vs desktop OS
  const major = (ua.match(/Chrome\/(\d+)/) || [])[1];
  if (major && !chUA.includes('v="' + major + '"')) return false; // version drift
  return true;
}

Related terms

What Is Browser Fingerprinting?
Browser fingerprinting is a technique that identifies and tracks a visitor by combining dozens of small, observable characteristics of their…
What Is Fingerprint Lie Detection?
Fingerprint lie detection is the practice of verifying that the signals a browser reports are internally consistent and untampered, rather t…
What Is Fingerprint Clustering?
Fingerprint clustering is the practice of grouping fingerprints from millions of real visitors by similarity, then rejecting any new visitor…
What Is Headless Browser Detection?
Headless browser detection is the set of probes anti-bot systems use to distinguish a headless or instrumented Chrome session from a real us…
Anti-Bot Vendor Detection Cheatsheet
A useful first step when working with any protected site you are authorized to access is identifying which anti-bot vendor sits in front of …
What Is Camoufox?
Camoufox is a fork of Firefox with anti-fingerprinting patches applied at the C++ build level. That phrase matters: most anti-fingerprinting…
What Is Anti-Bot Detection?
Anti-bot detection is the set of techniques websites use to tell automated traffic apart from real human visitors — and then block, challeng…
How Browser Fingerprinting Works
Browser fingerprinting is how a site combines signals — canvas, WebGL, audio, fonts, navigator probes, TLS (the encryption layer behind http…
How Do Websites Detect Web Scrapers?
Websites spot scrapers by gathering hundreds of small clues about each visitor, then scoring how human the whole picture looks. No single cl…
What Is a User Agent?
A user agent is a short text string a client sends in the User-Agent HTTP header to tell a server what software is making the request. Every…

Concept map

How Client Hints Fingerprinting connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Anti-Bot
Building map…

Frequently asked questions

What is the difference between the User-Agent string and Client Hints?

The UA string is one freeform line of text that Chrome is freezing and reducing to limit passive fingerprinting. Client Hints carry the same facts in a structured, opt-in form - as headers plus a JS API. The key detection point is that they must agree: the UA string, the Sec-CH-UA headers, and navigator.userAgentData all derive from one source on a real browser, so any mismatch stands out.

Do I need to send Sec-CH-UA headers from a Python scraper?

If you send a Chrome User-Agent, yes - real Chrome always sends Sec-CH-UA, Sec-CH-UA-Platform, and Sec-CH-UA-Mobile, so their absence flags you. Better still, impersonate a real Chrome build end to end (a TLS-impersonating client or a real browser) instead of hand-assembling headers, because the GREASE token and brand ordering are easy to get wrong.

What is GREASE in Sec-CH-UA?

GREASE is a deliberately fake brand entry (e.g. "Not_A Brand";v="24") that browsers include so servers cannot hardcode the brand list. Its exact text, version, and position vary by Chrome version. Anti-bot vendors know the real patterns, so a wrong or missing GREASE entry is a clear spoofing tell.

Last updated: 2026-05-31