UserAgentClientHints Module
The UserAgentClientHints module provides functionality for overriding user agent client hints in the browser. Client hints are a set of HTTP request headers that allow servers to request device and browser information, enabling responsive design and feature detection without relying solely on the User-Agent string.
Overview
The UserAgentClientHints module allows you to:
- Override user agent client hints (brands, platform, architecture, mobile flag, etc.)
- Emulate different browser brands and versions for testing
- Scope overrides to specific browsing contexts or user contexts
- Reset overrides to restore default browser behavior
This module complements the Emulation Module, which provides user agent string override. Client hints offer a more granular, structured way to control the information websites receive about the browser environment.
Accessing the Module
UserAgentClientHintsModule userAgentClientHints = driver.UserAgentClientHints;
Timeout and Cancellation
All commands in this module accept optional timeoutOverride and CancellationToken parameters. Use timeoutOverride to set a per-command timeout (defaults to BiDiDriver.DefaultCommandTimeout when omitted). Use CancellationToken for cooperative cancellation. See the API Design Guide for details and examples.
Setting Client Hints Override
Basic Override
SetClientHintsOverrideCommandParameters parameters =
new SetClientHintsOverrideCommandParameters();
parameters.ClientHints = new ClientHintsMetadata
{
Brands = new List<BrandVersion>
{
new BrandVersion("Chromium", "120.0"),
new BrandVersion("Google Chrome", "120.0")
},
Platform = "Windows",
PlatformVersion = "10.0",
Architecture = "x86",
Mobile = false
};
await driver.UserAgentClientHints.SetClientHintsOverrideAsync(parameters);
Console.WriteLine("Client hints override set");
Common Browser Brands
// Three complete profiles. Each SetClientHintsOverrideAsync call replaces the previous
// override, so pick the one you want to emulate rather than assigning all three in turn.
ClientHintsMetadata chromeOnWindows = new ClientHintsMetadata
{
Brands = new List<BrandVersion>
{
new BrandVersion("Chromium", "120.0"),
new BrandVersion("Google Chrome", "120.0")
},
FullVersionList = new List<BrandVersion>
{
new BrandVersion("Chromium", "120.0.6099.109"),
new BrandVersion("Google Chrome", "120.0.6099.109")
},
Platform = "Windows",
PlatformVersion = "10.0",
Architecture = "x86",
Model = "",
Mobile = false,
Bitness = "64"
};
ClientHintsMetadata firefoxOnMac = new ClientHintsMetadata
{
Brands = new List<BrandVersion>
{
new BrandVersion("Not_A Brand", "8"),
new BrandVersion("Firefox", "121.0")
},
Platform = "macOS",
PlatformVersion = "14.0",
Architecture = "arm",
Mobile = false
};
ClientHintsMetadata chromeOnAndroid = new ClientHintsMetadata
{
Brands = new List<BrandVersion>
{
new BrandVersion("Chromium", "120.0"),
new BrandVersion("Google Chrome", "120.0")
},
Platform = "Android",
PlatformVersion = "14.0",
Architecture = "arm",
Model = "Pixel 7",
Mobile = true
};
SetClientHintsOverrideCommandParameters parameters =
new SetClientHintsOverrideCommandParameters
{
ClientHints = chromeOnWindows, // or firefoxOnMac, or chromeOnAndroid
};
await driver.UserAgentClientHints.SetClientHintsOverrideAsync(parameters);
Client Hints Metadata Properties
The ClientHintsMetadata class supports the following properties:
| Property | Type | Description |
|---|---|---|
Brands |
List<BrandVersion>? |
Browser brand and version pairs (e.g., Chromium/120.0) |
FullVersionList |
List<BrandVersion>? |
Full version strings for each brand |
Platform |
string? |
Platform name (Windows, macOS, Android, Linux) |
PlatformVersion |
string? |
Platform version |
Architecture |
string? |
CPU architecture (x86, arm) |
Model |
string? |
Device model (for mobile) |
Mobile |
bool? |
Whether the device is mobile |
Bitness |
string? |
Pointer size (32, 64) |
Wow64 |
bool? |
Whether running under WOW64 (Windows) |
FormFactors |
List<string>? |
Device form factors |
All properties are optional. Omitted properties are not sent in the command and retain their default behavior.
Brands and FullVersionList are nullable and settable rather than read-only, because the emulation branches on whether the member is present: leaving one null omits it, so the browser's own value is reported, while setting it to an empty list sends [] and overrides that value with an empty one. FormFactors has the same shape for parity with them, but the emulated client hints the specification defines do not yet include form factors, so the specification gives that member no effect. Every other optional list in the library is read-only and omitted while empty; see API Design — Optional List Properties.
Resetting Client Hints
To clear the client hints override and restore default browser behavior, use the ResetClientHintsOverride static property:
await driver.UserAgentClientHints.SetClientHintsOverrideAsync(
SetClientHintsOverrideCommandParameters.ResetClientHintsOverride);
Console.WriteLine("Client hints override cleared");
Note: This command always requires explicit parameters. You must pass either ResetClientHintsOverride to clear or a configured SetClientHintsOverrideCommandParameters instance to set overrides.
Scoping Overrides
Target Specific Browsing Contexts
SetClientHintsOverrideCommandParameters parameters =
new SetClientHintsOverrideCommandParameters();
parameters.ClientHints = new ClientHintsMetadata
{
Brands = new List<BrandVersion> { new BrandVersion("Chromium", "120.0") },
Mobile = true
};
parameters.Contexts.Add(contextId);
await driver.UserAgentClientHints.SetClientHintsOverrideAsync(parameters);
Target Specific User Contexts
SetClientHintsOverrideCommandParameters parameters =
new SetClientHintsOverrideCommandParameters();
parameters.ClientHints = new ClientHintsMetadata
{
Platform = "Linux",
Architecture = "x86"
};
parameters.UserContexts.Add(userContextId);
await driver.UserAgentClientHints.SetClientHintsOverrideAsync(parameters);
While Contexts and UserContexts are left empty they are omitted from the command and the override applies to all contexts. Add entries to scope the override to specific browsing contexts or user contexts. The two are mutually exclusive: a command that names both is rejected with invalid argument.
Common Patterns
Pattern: Mobile Device Testing
// Emulate mobile client hints for responsive design testing
SetClientHintsOverrideCommandParameters parameters = new SetClientHintsOverrideCommandParameters();
parameters.ClientHints = new ClientHintsMetadata
{
Brands = new List<BrandVersion>
{
new BrandVersion("Chromium", "120.0"),
new BrandVersion("Google Chrome", "120.0")
},
Platform = "Android",
PlatformVersion = "14.0",
Architecture = "arm",
Model = "Pixel 7",
Mobile = true,
FormFactors = new List<string> { "Mobile" }
};
await driver.UserAgentClientHints.SetClientHintsOverrideAsync(parameters);
// Combine with Emulation module for full mobile emulation
await driver.BrowsingContext.SetViewportAsync(
new SetViewportCommandParameters
{
BrowsingContextId = contextId,
Viewport = new Viewport { Width = 412, Height = 915 },
DevicePixelRatio = 2.625
});
Pattern: Cross-Browser Brand Testing
// Test how a site behaves with different browser brands
Dictionary<string, ClientHintsMetadata> browserConfigs = new()
{
["Chrome"] = new ClientHintsMetadata
{
Brands = new List<BrandVersion>
{
new BrandVersion("Chromium", "120.0"),
new BrandVersion("Google Chrome", "120.0")
},
Platform = "Windows",
Mobile = false
},
["Edge"] = new ClientHintsMetadata
{
Brands = new List<BrandVersion>
{
new BrandVersion("Chromium", "120.0"),
new BrandVersion("Microsoft Edge", "120.0")
},
Platform = "Windows",
Mobile = false
},
["Safari"] = new ClientHintsMetadata
{
Brands = new List<BrandVersion>
{
new BrandVersion("Safari", "17.0")
},
Platform = "macOS",
Mobile = false
}
};
foreach (KeyValuePair<string, ClientHintsMetadata> config in browserConfigs)
{
Console.WriteLine($"\nTesting as {config.Key}");
SetClientHintsOverrideCommandParameters parameters =
new SetClientHintsOverrideCommandParameters();
parameters.ClientHints = config.Value;
await driver.UserAgentClientHints.SetClientHintsOverrideAsync(parameters);
await driver.BrowsingContext.NavigateAsync(
new NavigateCommandParameters(contextId, "https://example.com")
{ Wait = ReadinessState.Complete });
// Verify site behavior for this browser brand
EvaluateResult result = await driver.Script.EvaluateAsync(
new EvaluateCommandParameters(
"navigator.userAgentData?.brands?.map(b => b.brand).join(', ') ?? 'not supported'",
new ContextTarget(contextId),
true));
if (result is EvaluateResultSuccess success &&
success.Result is StringRemoteValue brandsValue)
{
string brands = brandsValue.Value;
Console.WriteLine($"Detected brands: {brands}");
}
}
Pattern: Verify Client Hints in Page
// Set override
SetClientHintsOverrideCommandParameters parameters =
new SetClientHintsOverrideCommandParameters();
parameters.ClientHints = new ClientHintsMetadata
{
Brands = new List<BrandVersion> { new BrandVersion("TestBrowser", "1.0") },
Platform = "TestOS",
Mobile = true
};
await driver.UserAgentClientHints.SetClientHintsOverrideAsync(parameters);
// Verify via User-Agent Client Hints API (JavaScript)
EvaluateResult result = await driver.Script.EvaluateAsync(
new EvaluateCommandParameters(
@"(async () => {
const ua = navigator.userAgentData;
if (!ua) return 'User-Agent Client Hints API not supported';
const hints = await ua.getHighEntropyValues(['brands', 'platform', 'mobile']);
return JSON.stringify(hints);
})()",
new ContextTarget(contextId),
true));
if (result is EvaluateResultSuccess success &&
success.Result is StringRemoteValue hintsValue)
{
string hintsJson = hintsValue.Value;
Console.WriteLine($"Client hints: {hintsJson}");
}
Best Practices
- Combine with other modules: For full device emulation, combine client hints with the Emulation module (user agent string, viewport meta behavior) and
BrowsingContext.SetViewportAsync(viewport size). - Reset between tests: Clear overrides between test cases to ensure isolation using
ResetClientHintsOverride. - Set before navigation: Apply overrides before navigating to pages that may use client hints for feature detection.
- Match brand and version: Ensure
BrandsandFullVersionListare consistent when both are used. - Verify support: The User-Agent Client Hints API is not supported in all browsers; check
navigator.userAgentDatabefore relying on hints in your tests.
Limitations
- Client hints override affects HTTP request headers and the
navigator.userAgentDataJavaScript API; the traditionalnavigator.userAgentstring is controlled by the Emulation module. - Support varies by browser; the client-hints override command is experimental in Chromium-based browsers and not yet available in Firefox or Safari (see the support matrix).
- Some websites may use additional detection methods beyond client hints.
Common Issues
Client Hints Not Applied
Problem: Website doesn't detect the overridden client hints.
Solution:
- Set the override before navigating to the page
- Ensure the page uses the User-Agent Client Hints API (
navigator.userAgentData) or that the server reads theSec-CH-*request headers - Verify browser support for client hints
Inconsistent with User-Agent String
Problem: Client hints and User-Agent string don't match.
Solution:
- Use the Emulation module's
SetUserAgentOverrideAsyncin conjunction withSetClientHintsOverrideAsync - Ensure brands, platform, and mobile flag align between both overrides
Override Not Clearing
Problem: Override persists after calling reset.
Solution:
- Use
SetClientHintsOverrideCommandParameters.ResetClientHintsOverrideexplicitly - Ensure you're not passing a parameters object with
ClientHintsset to an emptyClientHintsMetadata(use the reset property instead)
Next Steps
- Emulation Module: User agent string and viewport meta emulation
- Browsing Context Module: Viewport size emulation
- Browsing Context Module: Context management and navigation
- Network Module: Network interception and headers
- API Reference: Complete API documentation