
How Anti-Bot Systems Use Client Hints Detection to Catch Spoofed Browsers
In the ongoing cat-and-mouse game between browser automation tools and anti-bot systems, user agent client hints detection has become one of the most reliable methods for identifying fake or spoofed browser identities. Where anti-bot systems once relied primarily on checking the User-Agent string and running JavaScript challenges, the introduction of User-Agent Client Hints has given them a powerful new cross-validation tool — one that catches the vast majority of poorly implemented browser spoofing attempts.
The core principle is simple but devastating: Client Hints expose browser identity information through multiple independent channels (HTTP headers, JavaScript APIs, and legacy navigator properties), and a real browser maintains perfect consistency across all of them. A spoofed browser almost never does. Anti-bot systems exploit this by comparing values across channels, looking for the telltale mismatches that reveal automation or antidetect tools.
This guide provides a technical deep-dive into exactly how detection systems analyze Client Hints for inconsistencies, complete with detection code examples, real-world mismatch patterns, and strategies for maintaining consistency. For a broader understanding of the fingerprinting mechanism itself, see our companion article on client hints fingerprinting.
The Detection Surface: Where Anti-Bot Systems Look
The Three Layers of Browser Identity
A real Chrome browser exposes identity information through three distinct layers, all of which must be internally consistent:
Layer 1: HTTP Headers
User-Agent— The traditional (now reduced) UA stringSec-CH-UA— Brand list with major versionsSec-CH-UA-Mobile— Mobile indicatorSec-CH-UA-Platform— OS nameSec-CH-UA-Full-Version-List— Full version numbers (high-entropy)Sec-CH-UA-Platform-Version— OS version (high-entropy)Sec-CH-UA-Arch— CPU architecture (high-entropy)Sec-CH-UA-Bitness— CPU bitness (high-entropy)Sec-CH-UA-Model— Device model (high-entropy)
Layer 2: JavaScript API (navigator.userAgentData)
navigator.userAgentData.brands— Brand list (should match Sec-CH-UA)navigator.userAgentData.mobile— Mobile indicator (should match Sec-CH-UA-Mobile)navigator.userAgentData.platform— Platform (should match Sec-CH-UA-Platform)navigator.userAgentData.getHighEntropyValues()— Full version, architecture, etc.
Layer 3: Legacy Navigator Properties
navigator.userAgent— Traditional UA string (should match the User-Agent header)navigator.platform— Legacy platform identifiernavigator.appVersion— Legacy app version stringnavigator.oscpu— Firefox-specific OS identifier
Anti-bot systems collect data from all three layers and cross-reference every value. A single inconsistency is often enough to flag the session as suspicious.
Detection Technique #1: Sec-CH-UA vs. User-Agent Cross-Validation
What Detectors Check
The most fundamental detection technique compares the browser version and platform reported in the Sec-CH-UA headers against the information in the traditional User-Agent string. In a real Chrome browser, these values are generated from the same internal state and are always consistent.
Detection Code Example (Server-Side)
// Node.js server-side detection example
function detectClientHintsMismatch(headers) {
const results = { mismatches: [], riskScore: 0 };
const userAgent = headers['user-agent'] || '';
const secChUa = headers['sec-ch-ua'] || '';
const secChUaPlatform = headers['sec-ch-ua-platform'] || '';
const secChUaMobile = headers['sec-ch-ua-mobile'] || '';
// Check 1: Extract Chrome version from User-Agent
const uaChromeMatch = userAgent.match(/Chrome\/(\d+)\./);
const uaChromeVersion = uaChromeMatch ? uaChromeMatch[1] : null;
// Extract Chrome version from Sec-CH-UA
const chUaChromeMatch = secChUa.match(/"(?:Google Chrome|Chromium)";v="(\d+)"/);
const chUaChromeVersion = chUaChromeMatch ? chUaChromeMatch[1] : null;
if (uaChromeVersion && chUaChromeVersion &&
uaChromeVersion !== chUaChromeVersion) {
results.mismatches.push({
type: 'VERSION_MISMATCH',
detail: `UA says Chrome ${uaChromeVersion}, ` +
`Sec-CH-UA says ${chUaChromeVersion}`,
severity: 'CRITICAL'
});
results.riskScore += 90;
}
// Check 2: Platform consistency
const platformMap = {
'Windows NT': '"Windows"',
'Macintosh': '"macOS"',
'Linux': '"Linux"',
'CrOS': '"Chrome OS"',
'Android': '"Android"'
};
let uaPlatform = null;
for (const [uaKey, chValue] of Object.entries(platformMap)) {
if (userAgent.includes(uaKey)) {
uaPlatform = chValue;
break;
}
}
if (uaPlatform && secChUaPlatform &&
uaPlatform !== secChUaPlatform) {
results.mismatches.push({
type: 'PLATFORM_MISMATCH',
detail: `UA indicates ${uaPlatform}, ` +
`Sec-CH-UA-Platform says ${secChUaPlatform}`,
severity: 'CRITICAL'
});
results.riskScore += 95;
}
// Check 3: Mobile indicator consistency
const uaIsMobile = /Mobile|Android/.test(userAgent) &&
!/iPad/.test(userAgent);
const chIsMobile = secChUaMobile === '?1';
if (uaIsMobile !== chIsMobile) {
results.mismatches.push({
type: 'MOBILE_MISMATCH',
detail: `UA mobile: ${uaIsMobile}, ` +
`Sec-CH-UA-Mobile: ${chIsMobile}`,
severity: 'HIGH'
});
results.riskScore += 70;
}
return results;
}
Real-World Mismatch Patterns
| Pattern | What It Looks Like | Common Cause | Detection Confidence |
|---|---|---|---|
| Version mismatch | UA: Chrome/126, Sec-CH-UA: Chrome 127 | UA spoofed but Client Hints not updated | 99% |
| Platform mismatch | UA: Windows NT 10.0, Platform: “macOS” | Proxy or extension modifies UA only | 99% |
| Mobile mismatch | UA: Mobile Safari, Mobile: ?0 | Desktop browser spoofing mobile UA | 95% |
| Missing Client Hints | Chrome UA but no Sec-CH-UA header | Headless browser or old automation tool | 90% |
Detection Technique #2: Brand List Validation
How Brand List Detection Works
The Sec-CH-UA header contains a brand list — an array of brand-version pairs that includes the browser name, the engine name, and a GREASE (randomly formatted) brand. Anti-bot systems validate this brand list against known patterns for each Chrome version.
Detection Code Example (Client-Side)
// Client-side brand list validation
async function validateBrandList() {
const anomalies = [];
if (!navigator.userAgentData) {
anomalies.push({
type: 'NO_UA_DATA_API',
detail: 'navigator.userAgentData is undefined',
severity: 'HIGH',
note: 'Chrome 90+ should always have this API'
});
return anomalies;
}
const brands = navigator.userAgentData.brands;
// Check 1: Brand count — Chrome always sends exactly 3
if (brands.length !== 3) {
anomalies.push({
type: 'WRONG_BRAND_COUNT',
detail: `Expected 3 brands, got ${brands.length}`,
severity: 'CRITICAL'
});
}
// Check 2: Must contain both "Chromium" and a browser brand
const brandNames = brands.map(b => b.brand);
const hasChromium = brandNames.includes('Chromium');
const hasChrome = brandNames.includes('Google Chrome');
const hasEdge = brandNames.includes('Microsoft Edge');
const hasOpera = brandNames.includes('Opera');
if (!hasChromium) {
anomalies.push({
type: 'MISSING_CHROMIUM_BRAND',
detail: 'Chromium brand not found in brand list',
severity: 'CRITICAL'
});
}
if (!hasChrome && !hasEdge && !hasOpera) {
anomalies.push({
type: 'MISSING_BROWSER_BRAND',
detail: 'No known browser brand found',
severity: 'HIGH'
});
}
// Check 3: Chromium and browser version must match
if (hasChromium && hasChrome) {
const chromiumVersion = brands.find(
b => b.brand === 'Chromium'
).version;
const chromeVersion = brands.find(
b => b.brand === 'Google Chrome'
).version;
if (chromiumVersion !== chromeVersion) {
anomalies.push({
type: 'VERSION_DIVERGENCE',
detail: `Chromium v${chromiumVersion} ` +
`!= Chrome v${chromeVersion}`,
severity: 'CRITICAL'
});
}
}
// Check 4: GREASE brand validation
const knownBrands = [
'Chromium', 'Google Chrome', 'Microsoft Edge',
'Opera', 'Brave', 'Vivaldi'
];
const greaseBrand = brands.find(
b => !knownBrands.includes(b.brand)
);
if (!greaseBrand) {
anomalies.push({
type: 'MISSING_GREASE',
detail: 'No GREASE brand found in brand list',
severity: 'HIGH'
});
} else {
// GREASE brands should contain special characters
const hasSpecialChars = /[^a-zA-Z0-9 ]/.test(
greaseBrand.brand
);
if (!hasSpecialChars) {
anomalies.push({
type: 'INVALID_GREASE_FORMAT',
detail: `GREASE "${greaseBrand.brand}" ` +
`lacks special characters`,
severity: 'MEDIUM'
});
}
}
return anomalies;
}
GREASE Brand Patterns by Chrome Version
Anti-bot systems maintain databases mapping Chrome versions to their expected GREASE brand. Here’s a sample of the pattern:
| Chrome Version | Expected GREASE Brand | GREASE Version | Brand Order |
|---|---|---|---|
| 124 | "Not-A.Brand" |
"99" |
GREASE, Chromium, Chrome |
| 125 | "Not:A-Brand" |
"24" |
Chrome, Chromium, GREASE |
| 126 | "Not/A)Brand" |
"8" |
Chromium, GREASE, Chrome |
| 127 | "Not)A;Brand" |
"99" |
GREASE, Chromium, Chrome |
| 128 | "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128" |
"24" |
Chromium, GREASE, Chrome |
If a browser claims to be Chrome 127 but sends Chrome 124’s GREASE brand, the detection is immediate and definitive. This is one of the most common failures in antidetect browser implementations.
Detection Technique #3: HTTP vs. JavaScript API Consistency
The Cross-Layer Validation Approach
One of the most powerful detection strategies involves comparing what the browser reports via HTTP headers (captured server-side) against what the JavaScript APIs return (captured client-side). Anti-bot systems execute JavaScript on the client that reads navigator.userAgentData and sends the results back to the server for comparison.
Detection Code Example (Full-Stack)
// Client-side: Collect all identity signals
async function collectBrowserIdentity() {
const identity = {};
// Layer 2: navigator.userAgentData
if (navigator.userAgentData) {
identity.uaData = {
brands: navigator.userAgentData.brands,
mobile: navigator.userAgentData.mobile,
platform: navigator.userAgentData.platform
};
try {
const highEntropy = await navigator.userAgentData
.getHighEntropyValues([
'architecture', 'bitness', 'fullVersionList',
'model', 'platformVersion', 'wow64', 'formFactors'
]);
identity.highEntropy = highEntropy;
} catch (e) {
identity.highEntropyError = e.message;
}
}
// Layer 3: Legacy navigator
identity.legacy = {
userAgent: navigator.userAgent,
platform: navigator.platform,
appVersion: navigator.appVersion,
vendor: navigator.vendor,
maxTouchPoints: navigator.maxTouchPoints
};
return identity;
}
// Server-side: Cross-validate HTTP headers vs JS data
function crossValidate(httpHeaders, clientIdentity) {
const flags = [];
// Check: Sec-CH-UA-Platform (HTTP) vs
// navigator.userAgentData.platform (JS)
const httpPlatform = httpHeaders['sec-ch-ua-platform'];
const jsPlatform = clientIdentity.uaData?.platform;
if (httpPlatform && jsPlatform) {
// Remove quotes from HTTP header value
const cleanHttpPlatform = httpPlatform.replace(/"/g, '');
if (cleanHttpPlatform !== jsPlatform) {
flags.push({
type: 'HTTP_JS_PLATFORM_MISMATCH',
http: cleanHttpPlatform,
js: jsPlatform,
severity: 'CRITICAL'
});
}
}
// Check: navigator.platform (legacy) vs
// Sec-CH-UA-Platform (modern)
const legacyPlatform = clientIdentity.legacy?.platform;
const expectedLegacy = {
'Windows': ['Win32', 'Win64'],
'macOS': ['MacIntel', 'MacARM'],
'Linux': ['Linux x86_64', 'Linux armv81'],
'Chrome OS': ['CrOS'],
'Android': ['Linux armv8l', 'Linux armv81']
};
if (jsPlatform && legacyPlatform) {
const validLegacy = expectedLegacy[jsPlatform] || [];
if (!validLegacy.includes(legacyPlatform)) {
flags.push({
type: 'LEGACY_MODERN_PLATFORM_MISMATCH',
modern: jsPlatform,
legacy: legacyPlatform,
severity: 'CRITICAL'
});
}
}
// Check: Brand list consistency between HTTP and JS
const httpBrands = parseSecChUa(
httpHeaders['sec-ch-ua']
);
const jsBrands = clientIdentity.uaData?.brands;
if (httpBrands && jsBrands) {
const httpBrandSet = new Set(
httpBrands.map(b => `${b.brand}:${b.version}`)
);
const jsBrandSet = new Set(
jsBrands.map(b => `${b.brand}:${b.version}`)
);
const httpOnly = [...httpBrandSet].filter(
b => !jsBrandSet.has(b)
);
const jsOnly = [...jsBrandSet].filter(
b => !httpBrandSet.has(b)
);
if (httpOnly.length > 0 || jsOnly.length > 0) {
flags.push({
type: 'BRAND_LIST_DIVERGENCE',
httpOnly, jsOnly,
severity: 'CRITICAL'
});
}
}
return flags;
}
This cross-layer validation is extremely effective because modifying HTTP headers (through a proxy or middleware) doesn’t automatically change what the JavaScript APIs return, and vice versa. Many antidetect tools modify one layer but forget the other, creating the exact mismatches this code detects. For a comprehensive look at these and other bypass techniques, explore our guide on anti-bot detection bypass.
Detection Technique #4: Platform Version Validation
Checking Platform Version Plausibility
The Sec-CH-UA-Platform-Version hint reveals OS version information, and detectors validate that this value is plausible for the claimed platform. Each operating system has a specific version format and valid range:
// Platform version plausibility check
function validatePlatformVersion(platform, platformVersion) {
const flags = [];
const versionRules = {
'Windows': {
// Windows versions map:
// 1.0-10.0 = Win10, 13.0+ = Win11
pattern: /^\d{1,2}\.\d+\.\d+$/,
validRanges: [
{ min: '1.0.0', max: '10.0.0', os: 'Windows 10' },
{ min: '13.0.0', max: '16.0.0', os: 'Windows 11' }
],
invalidExamples: ['11.0.0', '12.0.0']
// These don't exist
},
'macOS': {
pattern: /^\d{2}\.\d+\.\d+$/,
validRanges: [
{ min: '13.0.0', max: '16.0.0',
os: 'macOS Ventura-future' }
]
},
'Linux': {
pattern: /^\d+\.\d+(\.\d+)?$/,
validRanges: [
{ min: '5.0', max: '7.0', os: 'Linux kernel' }
]
},
'Android': {
pattern: /^\d{1,2}(\.\d+){0,2}$/,
validRanges: [
{ min: '12', max: '17', os: 'Android' }
]
}
};
const rules = versionRules[platform];
if (!rules) return flags;
// Check format
if (!rules.pattern.test(platformVersion)) {
flags.push({
type: 'INVALID_VERSION_FORMAT',
detail: `"${platformVersion}" doesn't match ` +
`${platform} version format`,
severity: 'HIGH'
});
}
// Check for impossible Windows versions
if (platform === 'Windows') {
const major = parseInt(platformVersion.split('.')[0]);
if (major === 11 || major === 12) {
flags.push({
type: 'IMPOSSIBLE_WINDOWS_VERSION',
detail: `Windows platform version ` +
`${major}.x.x doesn't exist`,
severity: 'CRITICAL'
});
}
}
return flags;
}
Common Platform Version Errors
| Error | What Was Sent | Why It’s Wrong | What It Should Be |
|---|---|---|---|
| Windows 11 wrong mapping | Platform-Version: “11.0.0” | Windows 11 maps to 13.0.0+, not 11.0.0 | “13.0.0” or “14.0.0” or “15.0.0” |
| macOS version on Windows | Platform: “Windows”, Version: “14.5.0” | 14.5.0 is a macOS Sonoma version format | “10.0.0” or “15.0.0” |
| Missing version segments | Platform-Version: “15” | Windows versions use 3 segments: X.Y.Z | “15.0.0” |
| Android version on desktop | Platform: “Windows”, Version: “14” | Single-segment version is Android format | “15.0.0” |
Detection Technique #5: navigator.platform Cross-Reference
The Legacy Property Trap
Many antidetect tools and browser automation frameworks focus on spoofing the modern Client Hints APIs while forgetting about the legacy navigator.platform property. This property has existed since the earliest days of JavaScript and returns values like “Win32”, “MacIntel”, or “Linux x86_64”.
The critical detection insight: navigator.platform must be consistent with navigator.userAgentData.platform:
| userAgentData.platform | Valid navigator.platform Values | Invalid (Detectable) |
|---|---|---|
| “Windows” | “Win32” (yes, even on 64-bit Windows) | “MacIntel”, “Linux x86_64” |
| “macOS” | “MacIntel” (Intel), “MacARM” (Apple Silicon, newer Chrome) | “Win32”, “Linux x86_64” |
| “Linux” | “Linux x86_64”, “Linux armv8l” | “Win32”, “MacIntel” |
| “Chrome OS” | “CrOS x86_64”, “CrOS aarch64” | “Win32”, “MacIntel” |
| “Android” | “Linux armv8l”, “Linux armv81” | “Win32”, “MacIntel” |
Note the quirk: On 64-bit Windows, navigator.platform returns “Win32” (not “Win64”) for compatibility reasons. An antidetect tool that “helpfully” returns “Win64” when spoofing a 64-bit Windows identity is actually creating a detectable anomaly, since real Chrome never returns “Win64” for this property.
The maxTouchPoints Cross-Check
Another often-overlooked cross-reference: navigator.maxTouchPoints must be consistent with the platform and mobile indicator:
// Touch point validation
function validateTouchPoints(platform, mobile, touchPoints) {
const flags = [];
if (platform === 'Windows' && !mobile) {
// Desktop Windows: 0 (no touch) or typically
// 1-10 (touchscreen)
// Values > 10 are suspicious
if (touchPoints > 10) {
flags.push({
type: 'EXCESSIVE_TOUCH_POINTS',
detail: `Windows desktop with ` +
`${touchPoints} touch points`,
severity: 'MEDIUM'
});
}
}
if (platform === 'macOS') {
// macOS: always 0 (Apple doesn't have
// touchscreen Macs) — except for
// upcoming models in 2026+
if (touchPoints > 5) {
flags.push({
type: 'MACOS_TOUCH_ANOMALY',
detail: `macOS with ${touchPoints} touch points`,
severity: 'HIGH'
});
}
}
if (mobile && touchPoints === 0) {
// Mobile device with no touch? Suspicious
flags.push({
type: 'MOBILE_NO_TOUCH',
detail: 'Mobile device reports 0 touch points',
severity: 'HIGH'
});
}
return flags;
}
Detection Technique #6: Full Version List Analysis
Dissecting Sec-CH-UA-Full-Version-List
The Sec-CH-UA-Full-Version-List high-entropy hint provides the complete version string for each brand, including the four-segment version number (major.minor.build.patch). Anti-bot systems validate this data in several ways:
// Full version list validation
function validateFullVersionList(fullVersionList,
majorVersion) {
const flags = [];
if (!Array.isArray(fullVersionList)) return flags;
for (const entry of fullVersionList) {
// Skip GREASE entries
if (isGreaseBrand(entry.brand)) continue;
// Check: Major version in full version
// must match low-entropy brand version
const fullMajor = entry.version.split('.')[0];
if (fullMajor !== String(majorVersion)) {
flags.push({
type: 'FULL_VERSION_MAJOR_MISMATCH',
detail: `Brand ${entry.brand}: ` +
`full version ${entry.version} ` +
`major (${fullMajor}) != ` +
`expected (${majorVersion})`,
severity: 'CRITICAL'
});
}
// Check: Version format must be X.X.XXXX.XXX
const versionParts = entry.version.split('.');
if (versionParts.length !== 4) {
flags.push({
type: 'INVALID_VERSION_FORMAT',
detail: `${entry.brand} version ` +
`"${entry.version}" not 4-segment`,
severity: 'HIGH'
});
}
// Check: Build number should be in
// a plausible range
if (versionParts.length === 4) {
const build = parseInt(versionParts[2]);
if (build < 5000 || build > 7500) {
flags.push({
type: 'IMPLAUSIBLE_BUILD_NUMBER',
detail: `Build ${build} outside ` +
`expected range for Chrome`,
severity: 'MEDIUM'
});
}
}
// Check: Chromium and browser versions
// must match exactly
const chromium = fullVersionList.find(
e => e.brand === 'Chromium'
);
const browser = fullVersionList.find(
e => e.brand === 'Google Chrome' ||
e.brand === 'Microsoft Edge'
);
if (chromium && browser &&
browser.brand === 'Google Chrome' &&
chromium.version !== browser.version) {
flags.push({
type: 'CHROMIUM_CHROME_VERSION_DIVERGENCE',
detail: `Chromium ${chromium.version} != ` +
`Chrome ${browser.version}`,
severity: 'CRITICAL'
});
}
}
return flags;
}
Build Number Intelligence
Sophisticated anti-bot systems maintain databases of valid Chrome build numbers and their release dates. This allows them to detect:
- Fabricated build numbers: Build numbers that were never released by Google
- Temporal inconsistencies: A Chrome version that claims to be from the future, or an old build number combined with a new major version
- Impossible combinations: Build numbers that exist but were never associated with the claimed major version
How Send.win Helps You Master User Agent Client Hints Detection
Send.win makes User Agent Client Hints Detection simple and secure with powerful browser isolation technology:
- Browser Isolation – Every tab runs in a sandboxed environment
- Cloud Sync – Access your sessions from any device
- Multi-Account Management – Manage unlimited accounts safely
- No Installation Required – Works instantly in your browser
- Affordable Pricing – Enterprise features without enterprise costs
Try Send.win Free – No Credit Card Required
Experience the power of browser isolation with our free demo:
- Instant Access – Start testing in seconds
- Full Features – Try all capabilities
- Secure – Bank-level encryption
- Cross-Platform – Works on desktop, mobile, tablet
- 14-Day Money-Back Guarantee
Ready to upgrade? View pricing plans starting at just $9/month.
Detection Technique #7: Behavioral Analysis
Timing and Order of Hints
Beyond the static values, anti-bot systems analyze the behavioral aspects of how Client Hints are sent:
- First-request behavior: On the very first request to a domain, only low-entropy hints should be present. High-entropy hints should only appear after the server sends an
Accept-CHresponse. If high-entropy hints are present on the first request, the browser is likely pre-configured to send them (a signal of automation) - Hint persistence: After receiving
Accept-CH, hints should persist across subsequent requests within the same origin. Inconsistent hint presence across requests suggests middleware manipulation - getHighEntropyValues() timing: The JavaScript call to
getHighEntropyValues()returns a Promise that resolves almost instantly in real browsers. Unusually long resolution times can indicate interception or spoofing overhead
Comprehensive Detection Matrix
Here’s a consolidated view of all Client Hints detection signals and their reliability:
| Detection Signal | Implementation | Detection Confidence | False Positive Risk | Spoofing Difficulty |
|---|---|---|---|---|
| UA vs Sec-CH-UA version | Server-side | 99% | Very Low | Easy (if you know to do it) |
| Platform cross-reference | Server-side + Client-side | 99% | Very Low | Easy |
| GREASE brand validation | Server-side or Client-side | 95% | Low | Medium (requires version DB) |
| HTTP vs JS brand list | Full-stack | 98% | Very Low | Hard (requires engine-level spoofing) |
| navigator.platform mismatch | Client-side | 97% | Low | Easy (once aware) |
| Platform version format | Server-side | 90% | Low | Medium |
| Full version list build numbers | Server-side | 85% | Medium | Medium |
| Touch points cross-check | Client-side | 80% | Medium | Easy |
| First-request hint presence | Server-side | 75% | Medium | Hard |
| getHighEntropyValues() timing | Client-side | 60% | High | Hard |
Maintaining Perfect Client Hints Consistency
What Antidetect Browsers Must Get Right
For legitimate multi-account professionals, maintaining Client Hints consistency isn’t about evading security — it’s about ensuring that each browser profile presents a coherent, believable identity. This is especially important for professionals managing multiple advertising accounts, social media profiles, or e-commerce storefronts where inconsistent browser identity can trigger false fraud alerts.
The requirements for perfect consistency are:
- Version-accurate GREASE generation: Each Chrome version has a specific GREASE brand; the antidetect browser must generate the correct one
- All-layer synchronization: HTTP headers, JavaScript APIs, and legacy navigator properties must all agree
- Platform version correctness: The OS version must use the correct format and fall within the valid range for the claimed platform
- Build number validity: The Chrome build number must correspond to a real, released version
- Accept-CH flow compliance: High-entropy hints must not appear before the server requests them
- Touch points and form factor alignment: Device characteristics must match the claimed platform
Understanding how these consistency checks interact with other fingerprinting layers is crucial. Our guide on browser fingerprint explained covers the full stack of browser identity signals that must be coordinated. Additionally, anti-bot systems combine Client Hints with network-level signals like TLS fingerprinting JA3 for multi-layered detection.
🏆 Send.win Verdict
User agent client hints detection is one of the fastest-evolving areas of anti-bot technology. The number of cross-validation checks that modern systems perform — from GREASE brand matching to platform version format validation to HTTP-vs-JavaScript consistency — means that any antidetect tool that doesn’t handle Client Hints at the browser engine level is effectively broken. Send.win maintains perfect Client Hints consistency because it runs real, isolated Chrome instances in the cloud. Each profile generates genuine Sec-CH-UA headers, navigator.userAgentData responses, and legacy navigator properties from the same internal browser state — exactly as a real user’s browser would. There’s no patching, no interception, and no detectable inconsistency layer.
Try Send.win free today — every browser profile passes every Client Hints consistency check, guaranteed.
Frequently Asked Questions
What is user agent client hints detection?
User agent client hints detection is the process by which anti-bot and anti-fraud systems analyze the consistency of User-Agent Client Hints data to identify spoofed, automated, or manipulated browsers. These systems compare Sec-CH-UA HTTP headers against JavaScript API responses (navigator.userAgentData), traditional User-Agent strings, and legacy navigator properties to find mismatches that indicate a browser identity has been fabricated rather than genuine.
Why is Sec-CH-UA important for bot detection?
Sec-CH-UA is critically important for bot detection because it provides a structured, multi-layered browser identity that is very difficult to spoof consistently. Unlike the traditional User-Agent string (which is just one HTTP header), Client Hints expose identity through HTTP headers, a synchronous JavaScript API, and an asynchronous high-entropy API — all of which must report identical information. Any inconsistency between these layers is a strong signal that the browser has been automated or manipulated.
How do anti-bot systems detect GREASE brand spoofing?
Anti-bot systems maintain databases mapping each Chrome major version to its expected GREASE brand string, version number, and position in the brand list. Chrome uses a deterministic algorithm that changes the GREASE brand with each release. If a browser claims to be Chrome 127 but sends Chrome 124’s GREASE brand (e.g., “Not-A.Brand” instead of “Not)A;Brand”), the mismatch is immediately detected. Systems also check that the brand list contains exactly 3 entries with the correct ordering.
Can I pass Client Hints detection with a browser extension?
Browser extensions can modify HTTP headers, but they cannot reliably modify what the JavaScript navigator.userAgentData API returns. This creates a fundamental inconsistency: the Sec-CH-UA header might show one identity while navigator.userAgentData.brands reveals the real browser’s identity. Anti-bot systems specifically check for this HTTP-vs-JavaScript divergence, making extension-based spoofing detectable. Only solutions that modify the browser at the engine level can maintain consistency across all layers.
What is the most common Client Hints spoofing mistake?
The most common mistake is modifying the User-Agent string without updating Client Hints at all. Many legacy browser automation tools and simple UA-switching extensions only change navigator.userAgent and the User-Agent HTTP header, completely ignoring Sec-CH-UA, navigator.userAgentData, and other Client Hints surfaces. The second most common mistake is sending the wrong GREASE brand for the claimed Chrome version, which happens when antidetect tools use a static or incorrectly generated brand list.
How does platform version detection work?
Platform version detection validates that the Sec-CH-UA-Platform-Version value matches the expected format and range for the claimed operating system. For example, Windows version “11.0.0” doesn’t exist — Windows 10 maps to versions 1.0.0-10.0.0 and Windows 11 maps to 13.0.0+. Sending a macOS-formatted version (like “14.5.0”) with a Windows platform is immediately detectable. Anti-bot systems also check that the platform version is temporally plausible — a very new version number combined with a very old browser version is suspicious.
Do Firefox and Safari have similar Client Hints detection vulnerabilities?
Firefox and Safari handle this differently. Firefox has partial Client Hints support with significant restrictions on high-entropy hints, providing a smaller detection surface. Safari doesn’t implement Client Hints at all, so the absence of Client Hints from a Safari user agent is expected. However, if a browser claims to be Firefox or Safari via its User-Agent string but sends Sec-CH-UA headers (which these browsers don’t send), that itself is a critical detection signal.
How often do anti-bot systems update their Client Hints detection rules?
Major anti-bot providers like Cloudflare, Akamai, PerimeterX, and DataDome update their detection rules with every Chrome stable release — approximately every 4 weeks. The Chrome release cycle dictates the pace because each new version introduces a new GREASE brand, potentially changes the brand list order, and may add or modify Client Hints. Anti-bot systems also continuously analyze traffic patterns to identify new spoofing techniques and update their heuristics accordingly.
