Firefox's appVersion names the same operating system as its user agent
On a Gecko engine claiming a desktop system, the platform inside appVersion (Windows, Macintosh or X11) is the one the user agent, navigator.platform and navigator.oscpu name. N/A on other engines, on Firefox for Android (whose desktop mode legitimately pairs an X11 user agent with "5.0 (Android …)"), and when appVersion is not in the "5.0 (<platform>)" form (severity: warn).
What a detector infers
navigator.appVersion is compared against the user agent by appversion-vs-useragent on Blink and WebKit, where it is the UA minus its first token. That row stands down on Firefox, whose appVersion is a short "5.0 (Windows)", "5.0 (Macintosh)" or "5.0 (X11)". The short form still names an operating system, and in Firefox's source it comes from the same place as the rest.
Navigator::GetAppVersion builds it as "5.0 (" plus the HTTP handler's platform, a value fixed at compile time (Windows, Macintosh, X11, or Android; Ubuntu builds append "; Ubuntu"), and the UA's OS group and navigator.oscpu are built in the same function. With resistFingerprinting on, nsRFPService spoofs the UA OS, appVersion, oscpu and platform together, per build platform. So on a genuine Firefox the four cannot name different desktop systems.
Measured on 2026-09-24: Firefox 156 (normal, private, resistFingerprinting) and Playwright's Firefox 144 agree, and so does Camoufox 152 at launch level under a Windows, macOS or Linux persona. Two of its other paths do not. A per-context persona (NewContext) rewrites the user agent, platform and oscpu and leaves appVersion on the launch persona's system, so a Linux context reports "5.0 (Macintosh)" beside an X11 user agent and "Linux x86_64", in the page and in its workers.
A captured fingerprint preset (fingerprint_preset) never sets appVersion at all, so it reports the HOST's system: on a Windows machine, a Linux or macOS preset answers "5.0 (Windows)". A Firefox whose user agent alone was overridden to another system shows the same split.
How to resolve it
Rewrite appVersion together with the user agent, platform and oscpu. Its value is "5.0 (Windows)", "5.0 (Macintosh)" or "5.0 (X11)" for the system being claimed. A per-context persona or a captured preset that leaves it on another system names two operating systems at once.
Read in the wild by
Camoufox (measured)
Measured while establishing ground truth for this row, from a page script and a worker. Generated launch-level personas are coherent; a NewContext persona for a different system keeps the launch persona's appVersion, and a fingerprint preset keeps the host's.
cloverlabs-camoufox 0.6.0, browser 152.0.4-beta.30, Windows 11 host. NewContext(os="linux") over a macOS launch persona: UA "X11; Linux x86_64", platform and oscpu "Linux x86_64", appVersion "5.0 (Macintosh)". NewContext(os="macos") over a Windows launch persona: platform "MacIntel", appVersion "5.0 (Windows)". fingerprint_preset=True with os="linux" or "macos": appVersion "5.0 (Windows)". Workers report the same appVersion as the page.
camoufox.comCamoufox issues
Reports the fingerprint-preset path setting the user agent, platform and oscpu but never appVersion, so appVersion falls through to the host's system. Closed on 2026-09-06; measured still present in cloverlabs-camoufox 0.6.0, and the per-context path shows the same split.
daijro/camoufox#753Every attribution traces to a published artifact. See the sources and their limits.
Nearby checks in Navigator identity
- a worker resolves the same IANA timezone as the main thread
worker-main-timezone-coherenceA browser has one host timezone, and the specification ties both the main-thread global and every worker global to it, so a real browser… - a second browser target resolves the same timezone as the window
timezone-shared-worker-vs-windowThis audit already compares the window's timezone against a dedicated worker. - the keyboard layout maps the OEM keys the way one real Windows layout would
keyboard-layout-single-originA keyboard layout is not a set of independent key mappings — it is one artifact the operating system hands over whole. - navigator.appVersion is the user agent minus its leading token
appversion-vs-useragentnavigator.appVersion is not an independent fact. In Blink and WebKit it is computed from the User-Agent at read time — literally the UA with… - in-app browser bridge objects agree with the browser being claimed
webview-wrapper-markers-vs-uaA deployed commercial agent enumerates a short list of window globals that are not web platform features at all. - the URL parser follows the specification exactly
url-parser-spec-conformanceThe URL specification pins the exact result of a handful of awkward parses, and it pins them for every engine and every operating system… - Trusted Types hands back the branded object its own type demands
trusted-types-value-invariantTrusted Types is a typed API, not a boolean feature flag, and that is the whole of the check. - the user agent on the wire and the one JavaScript reports describe the same browser
ua-wire-vs-navigatorYour user agent is asserted on two channels built by different layers: the network stack writes the header, and the renderer answers…
Clearcote is a browser built for fingerprint coherence
It is a Chromium fork, maintained by the same people who wrote this reference. It ships as a compiled browser rather than as a stealth script injected into someone else's — which is a description of how it is built, and is not an argument about how it behaves on this check.
This audit takes no position on how Clearcote scores on Navigator identity checks, on this one, or anywhere else. It has no baseline corpus of other people's fingerprints to rank you against and no vendor scoreboard — nearly every check is self-referential, asking one browser the same question through two independent APIs and reporting whether both answers can be true at once. It runs identically on any browser, including ours. Run it on yours and read the result yourself.
See the other checks in Navigator identity — the family gecko-appversion-os-vs-ua belongs to.
