Browser Brand Mismatches in Chrome, Edge, and Brave
Why User-Agent, Client Hints, and navigator.userAgentData must agree on one browser brand, and how to set and check a Chrome, Edge, Brave, or Opera identity.
BotBrowser Team
Prefer the maintained product doc?
This article has a matching page in the docs center. Use the docs for the canonical setup flow, current flags, and long-term reference.
Why a mixed browser brand is easy to notice
A browser brand is the vendor name a browser reports about itself: Google Chrome, Microsoft Edge, Brave, Opera, or plain Chromium. The brand appears in several places at once, and the answer to "which brand is this?" has to be the same in every one of them. A session that reports Microsoft Edge in the User-Agent header but still lists only Google Chrome in Sec-CH-UA or in navigator.userAgentData gives two different answers to one question.
This matters for privacy and for plain usability. Websites and analytics services use brand information to segment visitors, to decide which features or prompts to offer, and to group sessions that look alike. When the signals disagree, the disagreement itself makes the browser more distinctive than any single brand would, which is the opposite of what a privacy-minded configuration is meant to achieve. It also causes ordinary problems, such as a page offering an Edge-specific prompt to a session whose Client Hints say Chrome.
Two groups of readers run into this most often. Teams that test how a product behaves for Edge or Brave users need the browser to identify itself as the brand they are testing. People who keep several separate browsing identities for privacy need each identity to stay stable and internally consistent on its own. Both goals depend on consistency rather than on any single value, and both are easy to undermine with a partial change.
Three questions are worth asking before you change a brand:
- Which surfaces report a brand, and do they all change together?
- Does the brand-specific version belong to the same Chromium major version as the rest of the identity?
- Do the profile, network, locale, and platform choices still describe one coherent browser?
The surfaces that carry one brand identity
In a Chromium-based browser, the brand is expressed through the User-Agent string, the Client Hints headers, the navigator.userAgentData API, the order and shape of the brand list, and the version numbers attached to each entry. Each one is described below.
The User-Agent string
The User-Agent request header and navigator.userAgent carry the brand as a product token at the end of the string. Every Chromium-based browser keeps the Chrome/ and Safari/537.36 tokens for compatibility, so the vendor token is the part that differs:
- Chrome:
Mozilla/5.0 ... Chrome/142.0.7444.60 Safari/537.36 - Edge:
Mozilla/5.0 ... Chrome/142.0.7444.60 Safari/537.36 Edg/142.0.3595.65 - Brave:
Mozilla/5.0 ... Chrome/142.0.7444.60 Safari/537.36 - Opera:
Mozilla/5.0 ... Chrome/142.0.7444.60 Safari/537.36 OPR/<opera version>
Brave has no vendor token of its own in this string, so its brand shows up in Client Hints instead. That is one reason a string-only check is not enough to tell what a browser reports.
The Client Hints headers
Client Hints are the structured replacement for parsing the User-Agent string. The mechanism is defined in RFC 8942, and the user agent hints are documented on MDN. A small set of low-entropy hints, including Sec-CH-UA, is sent on requests by default. More detailed values, such as Sec-CH-UA-Full-Version-List, are sent only after a server asks for them with an Accept-CH response header. The Sec-CH-UA value lists brand tokens with major versions:
- Chrome:
"Chromium";v="142", "Google Chrome";v="142", "Not:A-Brand";v="99" - Edge:
"Chromium";v="142", "Microsoft Edge";v="142", "Not:A-Brand";v="99" - Brave:
"Chromium";v="142", "Brave";v="142", "Not:A-Brand";v="99"
The navigator.userAgentData API
The JavaScript side of the same information is navigator.userAgentData. Its brands property mirrors the low-entropy Sec-CH-UA list, and getHighEntropyValues() returns a promise with the detailed fields, such as fullVersionList, platform, and platformVersion. Whatever the header says, this API should say too:
navigator.userAgentData.brands;
// Chrome: [{brand: "Chromium", version: "142"}, {brand: "Google Chrome", version: "142"}, ...]
// Edge: [{brand: "Chromium", version: "142"}, {brand: "Microsoft Edge", version: "142"}, ...]
Because the high-entropy values are returned only on request, a mismatch can stay hidden until a site asks for them. A configuration that looks right on the first page load may still contradict itself later, which is why the checks below cover both the default values and the detailed ones.
Token order, GREASE entries, and version cadence
Chromium adds an intentionally meaningless "GREASE" entry to the brand list so that servers do not depend on a fixed list format. Its text and its position vary. According to the BotBrowser documentation, the brands array carries proper GREASE tokens, so the list in Sec-CH-UA and the list in navigator.userAgentData.brands should match each other, in the same order.
Version cadence is the other detail that is easy to get wrong. Chrome, Edge, and Opera release on their own schedules. Chrome and Edge share the Chromium major version, but their full version numbers differ: Chrome 142 might be 142.0.7444.60, while the matching Edge build is 142.0.3595.65. Opera's brand version follows Opera's own numbering rather than Chromium's. For Chrome and Edge, a brand entry whose full version does not belong to the Chromium major version in the rest of the identity is a mismatch, even if every token name is right.
Why a User-Agent override alone falls short
Automation frameworks make the string easy to change. Playwright accepts a userAgent option when you create a context, and Puppeteer offers page.setUserAgent():
// Playwright
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 ... Edg/142.0.3595.65',
});
// Puppeteer
await page.setUserAgent('Mozilla/5.0 ... Edg/142.0.3595.65');
A string-only override changes the header and navigator.userAgent. Unless the Client Hints metadata is supplied separately, the other surfaces keep reporting the original brand:
Sec-CH-UAstill lists the original brand tokens.navigator.userAgentData.brandsstill returns the original list.Sec-CH-UA-Full-Version-Liststill carries the original version numbers.- The values returned by
getHighEntropyValues()still describe the original browser.
The result is an identity that says Edge in one place and Chrome in another. Supplying the metadata by hand closes part of the gap, but it moves the work to you: the token order, the GREASE entry, the full versions, and every page, worker, and request have to be kept in step manually, and a hand-written list is easy to get slightly wrong.
A hand-maintained override also ages badly. When the Chromium major version moves, every hard-coded string and version number in a script becomes stale, and the next update can leave the User-Agent on one version while the Client Hints stay on another. A single brand setting that the browser applies everywhere avoids that bookkeeping.
Any approach that rewrites one surface at a time has the same shape of problem. A header rule in an extension, a string override in a framework, and a script that edits one property each cover their own surface, and nothing guarantees that the surfaces still agree. The reliable fix is to choose the brand once, at the start, and have every surface derive from that choice.
Setting the brand at launch with BotBrowser
BotBrowser's --bot-browser-brand flag (ENT Tier2; the webview value requires ENT Tier3) selects the brand when the browser starts. According to the BotBrowser documentation, setting it adjusts the User-Agent string, the navigator.userAgentData brands list, the Client Hints headers, and the related values together, and the result stays consistent across the main thread, Workers, and HTTP request headers.
| Surface | Follows the selected brand |
|---|---|
User-Agent header | Yes |
navigator.userAgent | Yes |
Sec-CH-UA header | Yes |
Sec-CH-UA-Full-Version-List | Yes |
navigator.userAgentData.brands | Yes |
getHighEntropyValues() | Yes |
| GREASE entry | Yes |
Supported brand values
| Brand | Flag value | Notes |
|---|---|---|
| Chrome | chrome | Default Chromium-based identity |
| Edge | edge | Microsoft Edge with the Edg/ token |
| Brave | brave | Brave browser identity |
| Opera | opera | Opera with the OPR/ token |
| Chromium | chromium | Plain Chromium identity without vendor branding |
| WebView | webview | Android WebView (ENT Tier3) |
Matching the brand version to the Chromium version
When you switch to Edge or Opera, the vendor's full version has to fit the Chromium version in the profile. Two flags control this, and the documentation recommends that both share the same major version number. Opera's own OPR/ number still follows Opera's numbering:
--bot-brand-full-versionsets the brand-specific full version, such as Edge's142.0.3595.65.--bot-ua-full-versionsets the Chromium full version, such as142.0.7444.60.
chrome --bot-profile="path/to/profile.enc" \
--bot-browser-brand=edge \
--bot-ua-full-version=142.0.7444.60 \
--bot-brand-full-version=142.0.3595.65
Launch examples for Edge, Brave, and Opera
On the command line, each brand is a single flag next to the profile. Pair it with a timezone, locale, and network route that suit the identity you are presenting:
# Edge identity, US locale
chrome --bot-profile="path/to/profile.enc" \
--bot-browser-brand=edge \
--bot-timezone=America/New_York \
--bot-locale=en-US
# Brave identity, UK locale
chrome --bot-profile="path/to/profile.enc" \
--bot-browser-brand=brave \
--bot-timezone=Europe/London \
--bot-locale=en-GB
# Opera identity
chrome --bot-profile="path/to/profile.enc" \
--bot-browser-brand=opera
Playwright and Puppeteer pass the same flags through their args option. The documentation notes that the brand flag belongs in args, not in a framework-level option, which is a common reason for a brand that does not change:
const { chromium } = require('playwright-core');
const browser = await chromium.launch({
executablePath: 'path/to/botbrowser/chrome',
headless: false,
args: ['--bot-profile=path/to/profile.enc', '--bot-browser-brand=edge', '--bot-brand-full-version=142.0.3595.65'],
});
A worked example shows what to expect. Suppose the profile uses Chromium 142 and you want an Edge identity. You launch with --bot-browser-brand=edge, --bot-ua-full-version=142.0.7444.60, and --bot-brand-full-version=142.0.3595.65. The User-Agent string then ends with Edg/142.0.3595.65, Sec-CH-UA lists Microsoft Edge next to Chromium with major version 142, and fullVersionList shows the Chromium and Edge full versions as separate entries. If any of those three disagrees with the others, one of the version flags is missing or inconsistent.
WebView identity
An Android WebView identity (ENT Tier3) needs more than the brand. Combine the webview brand with a User-Agent template and the Android platform settings from an Android profile. Placeholders such as {platform-version} and {model} are filled in from the matching flags, and BotBrowser generates the matching navigator.userAgentData and Client Hints values:
chrome --bot-profile="path/to/android-profile.enc" \
--bot-browser-brand=webview \
--user-agent="Mozilla/5.0 (Linux; Android {platform-version}; {model} Build/TP1A.220624.021; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/{ua-full-version} Mobile Safari/537.36" \
--bot-platform=Android \
--bot-platform-version=13 \
--bot-model=SM-G991B \
--bot-mobile=true
Keeping several brands apart
When one machine serves several identities, give each its own profile and its own user data directory, and set the brand per identity so that nothing carries over between them:
chrome --bot-profile="profiles/chrome-user.enc" \
--bot-browser-brand=chrome \
--user-data-dir="/tmp/chrome-session"
chrome --bot-profile="profiles/edge-user.enc" \
--bot-browser-brand=edge \
--user-data-dir="/tmp/edge-session"
The documentation also describes setting a different brand for each browser context at creation time (ENT Tier3). In both cases the brand is chosen when the instance or context is created, so changing it means starting a new one.
Checking that one brand shows up everywhere
After launching, you can verify the result in the browser itself, with no extra tooling. Open the developer tools on any page and work through this list:
- The
User-Agentstring contains the right vendor token (Edg/for Edge,OPR/for Opera). - The
Sec-CH-UArequest header lists the expected brand tokens. navigator.userAgentData.brandsmatches the header.fullVersionListshows the brand-specific version next to the Chromium version, and, for Chrome and Edge, both share the same major number. Opera's brand version follows Opera's own numbering.- No other brand name appears, apart from the Chromium base token that every Chromium-based brand includes.
For the script-visible values, paste three lines into the Console:
navigator.userAgent;
navigator.userAgentData.brands;
await navigator.userAgentData.getHighEntropyValues(['fullVersionList', 'platform', 'platformVersion']);
For the header, open the Network panel, select a top-level HTTPS request, and read the request headers. Detailed headers such as Sec-CH-UA-Full-Version-List appear only after the site requests them with Accept-CH, so their absence on a first request is expected and is not a mismatch. If your pages start workers, run the same console lines in the worker's context to confirm it reports the same brand as the page.
A passing checklist shows that the browser is internally consistent. It does not show how any particular site will treat the session, because sites combine many inputs and change their own rules over time. Use the checklist to confirm your own configuration, and keep the rest of the identity, such as the profile, the network route, and the locale, equally consistent.
When something looks wrong, the symptom usually points to the cause:
| Symptom | Likely cause | What to adjust |
|---|---|---|
The UA string says Edg/ but the brands list names only Google Chrome | Only the string was changed | Set the brand at launch instead of overriding the string |
The Edge version in fullVersionList looks like Chrome's | The brand-specific version was not set | Add --bot-brand-full-version |
| For Edge, the Chromium major differs between the UA string and the brand list | The two version flags do not share a major | Align --bot-ua-full-version and --bot-brand-full-version with the profile |
| The brand never changes | The flag was given as a framework option | Pass it inside args |
| The brand differs from what the profile says | Command-line flags take priority over the profile | Remove the flag, or update the profile |
Repeat the check after every change to the profile, the Chromium version, or the brand flags, because each of them can move one surface without moving the others.
Choosing a brand and planning around the limits
Treat the brand as one part of a complete identity rather than a free-standing switch:
- Keep the versions aligned. Use
--bot-brand-full-versionso the vendor version belongs to the Chromium major version in your profile. - Start from a suitable profile. A profile captured from a real Edge browser, combined with
--bot-browser-brand=edge, gives the most coherent result. - Match the surrounding settings. Brand, timezone, locale, language, and network route should describe the same kind of user.
- Keep one brand for the life of an identity. Changing the brand between visits to the same site creates an inconsistent history.
- Re-run the checklist after browser or profile updates.
You do not always need the flag. A loaded profile already carries a coherent brand, so leaving --bot-browser-brand unset is the simplest way to stay consistent. Use the flag when you have a reason to present a different brand, for example to reproduce an Edge-only support case or to give a second identity its own brand, and then confirm the version flags as well. Every extra override is one more value that has to stay in step with the rest, so change only what the task requires.
A few practical questions come up often:
- Does brand switching change the feature surface? No. It updates identity values, not the browser's interface. Brand-specific features such as Brave's shields or Edge's sidebar are not emulated.
- Can any brand be used with any profile? Yes. The flag overrides the brand in the profile, and command-line flags have the highest priority.
- What happens without the flag? The brand stored in the loaded profile is used, and Chrome is the default when the profile has none.
- Does the brand affect extensions? The documentation does not describe any effect on extensions, so test the extensions you depend on after a brand change.
- Can the brand change while the browser runs? No. It is set at launch, or when a context is created, so use a new instance for a different brand.
- Is WebView different? Yes. It also needs the platform, model, and mobile settings shown above.
BotBrowser supports switching the browser brand at launch with --bot-browser-brand (chrome, edge, brave, opera, chromium, or webview) and setting a brand-specific version with --bot-brand-full-version, so the User-Agent, navigator.userAgentData brands, and Client Hints headers change together instead of drifting apart. BotBrowser does not emulate brand-specific UI or features. It cannot change the brand of a running instance or context, since the brand is set when an instance or context is created, and it cannot guarantee how a site treats the resulting identity.
For User-Agent and Client Hints details, see User Agent Control and Client Hints. For combining brand identity with geographic settings, see Timezone, Locale, and Language Configuration. For multi-identity setups with different brands, see Multi-Account Browser Isolation.
Sources
Related Articles
Take BotBrowser from research to production
The guides cover the model first, then move into cross-platform validation, isolated contexts, and scale-ready browser deployment.