# Firefox

> Stock Firefox is read without exclusions; with Fingerprinting Protection three components are left out. A few Chromium-only APIs are missing, and WebGL strings come from the plain renderer enums.

Source: https://whiskerjs.com/browsers/firefox/ (last updated 2026-10-26)

> **In plain words:**
>
> In a normal Firefox, WhiskerJS reads almost everything and leaves nothing out on purpose. A few measurements only exist in Chrome-like browsers, so they come back as “unavailable”. Firefox has two privacy features that change what pages see. Fingerprinting Protection (on in private windows, and rolling out to normal windows) adds noise to canvas and WebGL reads: WhiskerJS notices that with a tiny test drawing and leaves those three signals out. The stricter resistFingerprinting mode makes Firefox report fixed values; WhiskerJS only treats a Firefox as being in that mode when every sign of it is present, so an ordinary Firefox is not mistaken for it.

## At a glance

- Browser class: `firefox` (a `Firefox/<number>` token in the user agent, or `mozInnerScreenX` on the window); `firefox_fpp` when Fingerprinting Protection alters canvas or WebGL readback.
- Excluded components: none in stock Firefox; 3 under Fingerprinting Protection (`canvas`, `screen_frame`, `webgl_image`, ruleId `firefox_fpp`).
- Canvas id: stable across sessions since signalsVersion 9 (the pixels are read with `getImageData()`, which Firefox does not randomise).
- Unavailable by design: `device_memory`, `heap_limit`, `ua_client_hints`, `ua_versions`, `keyboard_layout`.
- Skipped on purpose on desktop: `audio_latency`.
- Lab sample: none for Firefox yet.

## What works

- **Graphics**: [`canvas`](https://whiskerjs.com/signals/canvas/) and [`webgl_image`](https://whiskerjs.com/signals/webgl-image/) with the double render and the known-answer probes, [`webgl_params`](https://whiskerjs.com/signals/webgl-params/), and [`webgl_renderer`](https://whiskerjs.com/signals/webgl-renderer/) and [`webgl_vendor`](https://whiskerjs.com/signals/webgl-vendor/). In Firefox, WhiskerJS does not request `WEBGL_debug_renderer_info`, because Firefox prints a deprecation warning when it is requested; `gl.RENDERER` and `gl.VENDOR` already carry the unmasked strings there.
- **[`vendor`](https://whiskerjs.com/signals/vendor/)**: Firefox reports an empty `navigator.vendor`, which WhiskerJS reports as the plain value `empty`.
- Offline audio, fonts (both methods), font preferences, Math, codecs, plugins (typically a fixed PDF list), locale, storage presence, permission states and the display preference queries.
- [`webgpu`](https://whiskerjs.com/signals/webgpu/) where the Firefox version and platform offer `navigator.gpu`; otherwise `unavailable` / `no_api`.

## What is unavailable or blocked

| Component | Status | Why |
| --- | --- | --- |
| [`device_memory`](https://whiskerjs.com/signals/device-memory/) | `unavailable` / `no_api` | no `navigator.deviceMemory` |
| [`heap_limit`](https://whiskerjs.com/signals/heap-limit/) | `unavailable` / `no_api` | no `performance.memory` |
| [`ua_client_hints`](https://whiskerjs.com/signals/ua-client-hints/), [`ua_versions`](https://whiskerjs.com/signals/ua-versions/) | `unavailable` / `no_api` | no `navigator.userAgentData` |
| [`keyboard_layout`](https://whiskerjs.com/signals/keyboard-layout/) | `unavailable` / `no_api` | no `navigator.keyboard` |
| [`audio_latency`](https://whiskerjs.com/signals/audio-latency/) | `unavailable` / `skipped` | on desktop, creating an `AudioContext` without a user gesture prints a console warning |

Firefox for Android has an Android user agent, so `audio_latency` is read there. [`apple_pay`](https://whiskerjs.com/signals/apple-pay/) and [`private_click`](https://whiskerjs.com/signals/private-click/) report the plain value `none`.

## What WhiskerJS excludes and why

Nothing in stock Firefox. The `firefox` row of the exclusion table is empty.

### Fingerprinting Protection (`firefox_fpp`)

Firefox Fingerprinting Protection randomises canvas and WebGL readback per site and session and collapses the available screen area to the screen. It is on in private windows and, since Firefox 151, is rolling out to normal windows, starting with canvas randomisation. WhiskerJS does not read the setting. It classes a Firefox as `firefox_fpp` when it is not resistFingerprinting and the canvas or WebGL [known-answer probe](https://whiskerjs.com/signals/noise-profile/) came back altered. Then:

| Component | Why it is left out |
| --- | --- |
| [`canvas`](https://whiskerjs.com/signals/canvas/) | canvas readback is randomised per site and session |
| [`screen_frame`](https://whiskerjs.com/signals/screen-frame/) | the available screen area is collapsed to the screen |
| [`webgl_image`](https://whiskerjs.com/signals/webgl-image/) | WebGL readback is randomised per site and session |

All three report `excluded` with ruleId `firefox_fpp`. Stock Firefox reads the known answer back exactly, also under automation and with a screen that has no taskbar, so it stays `firefox`. Audio noise alone does not make the class (the `audio` component then turns `noisy` on its own). A stock Firefox with a canvas-noise extension is also classed `firefox_fpp`, which is right for the exclusions: its canvas is randomised too. When Firefox turns Fingerprinting Protection on in normal windows, `browser_class` changes and so does the id, once.

Before signalsVersion 9, stock Firefox had a second problem: Firefox 155 randomises `toDataURL()` per session while `getImageData()` stays exact, and the `canvas` component hashed `toDataURL()`, so stock Firefox gave a new id in every new browser context. Since signalsVersion 9 `canvas` reads the pixels and the Firefox id holds across sessions. The [previous id](https://whiskerjs.com/docs/identifiers/#previous-ids-linking-across-a-version-bump) for this bump will rarely match a stored Firefox id, because the version 8 id was not stable there.

### resistFingerprinting

Only when Firefox runs with `privacy.resistFingerprinting` does the `firefox_rfp` row apply, and the test for it is deliberately strict. Each trait alone (a UTC time zone, a screen the size of the window, a pixel ratio of 1) is common in stock Firefox, so all of them must hold at once, and a browser that reports `navigator.webdriver` as `true` is never classed `firefox_rfp`.

That strictness is the fix of signalsVersion 5: before it, stock Firefox under automation such as Playwright (UTC, screen equal to the viewport) was wrongly classed `firefox_rfp` and lost nine components. A Firefox run under Playwright in a test container, at signalsVersion 5, was classed `firefox` and raised only `automation_webdriver`; since signalsVersion 8 Playwright with an exposed function also raises `exposed_binding`. See [What’s new](https://whiskerjs.com/whats-new/) and [Firefox resistFingerprinting and Tor Browser](https://whiskerjs.com/browsers/firefox-rfp-and-tor/).

## Integrity flags to expect

- [`engine_mismatch`](https://whiskerjs.com/docs/integrity/#engine_mismatch) checks that a Firefox class behaves like Gecko: a 37-character `eval` source and `navigator.productSub` `20100101`. A user agent claiming Firefox in another engine raises it; so can user-agent switcher extensions.
- [`automation_webdriver`](https://whiskerjs.com/docs/integrity/#automation_webdriver) under Selenium, Playwright or similar.
- [`native_tampering`](https://whiskerjs.com/docs/integrity/#native_tampering), [`prototype_lie`](https://whiskerjs.com/docs/integrity/#prototype_lie) and [`navigator_own_property`](https://whiskerjs.com/docs/integrity/#navigator_own_property) with privacy or anti-fingerprinting add-ons.
- [`software_renderer`](https://whiskerjs.com/docs/integrity/#software_renderer) where WebGL runs in software, for example llvmpipe on some Linux setups.
- [`exposed_binding`](https://whiskerjs.com/docs/integrity/#exposed_binding) under Playwright with an exposed function. Since signalsVersion 9 it reads window properties through their descriptors, so Firefox no longer prints deprecation warnings (`Window.fullScreen`, `InstallTrigger`).
- [`ua_ch_mismatch`](https://whiskerjs.com/docs/integrity/#ua_ch_mismatch) is never checked: Firefox has no `navigator.userAgentData`. [`media_query_mismatch`](https://whiskerjs.com/docs/integrity/#media_query_mismatch) does not run for the `firefox_fpp` class.
- Fingerprinting Protection’s noise is never a flag: it shows in `noise_profile`, `canvas_readback` and the excluded components.

## Early lab observations

There is no lab data for Firefox yet. The early, small sample at signalsVersion 1 covered Mac Chrome, Mac Safari, iPhone Chrome and iPhone Safari only. The table below comes from the code.

## Testing it yourself

Open the [live demo](https://whiskerjs.com/demo/) in Firefox and check `browser_class`: it should say `firefox`, or `firefox_fpp` in a private window or with Fingerprinting Protection on (then `noise_profile` shows which probe saw noise). Run it again after a Firefox update and see which structuredId group moved. The [lab mode](https://whiskerjs.com/demo/#lab) copies one line per run. Nothing is sent anywhere.

## Support table for Firefox

Expected status of every component in this browser at signalsVersion 9, from [the support matrix](https://whiskerjs.com/browsers/support-matrix/) (updated 2026-10-26). Basis "code" means expected from the library code and documentation, not yet measured; "lab" means seen in the early lab sample.

| Component | Status | Basis | Note |
| --- | --- | --- | --- |
| [`apple_pay`](https://whiskerjs.com/signals/apple-pay/) | none | code | No ApplePaySession: reports the plain value none. |
| [`audio`](https://whiskerjs.com/signals/audio/) | ok | code | noisy / seeded_noise if the audio known-answer probe sees noise. |
| [`audio_latency`](https://whiskerjs.com/signals/audio-latency/) | varies | code | Skipped on desktop; Firefox for Android has an Android user agent, so it is read there. |
| [`browser_class`](https://whiskerjs.com/signals/browser-class/) | ok | code | Class firefox; firefox_fpp when Fingerprinting Protection alters canvas or WebGL readback. |
| [`canvas`](https://whiskerjs.com/signals/canvas/) | varies | code | Excluded (firefox_fpp) when Fingerprinting Protection is on: in private windows, and in normal windows as Firefox 151 and later roll it out. Otherwise ok. |
| [`canvas_readback`](https://whiskerjs.com/signals/canvas-readback/) | varies | code | exact, or altered with Fingerprinting Protection or a canvas-noise extension. |
| [`color_depth`](https://whiskerjs.com/signals/color-depth/) | ok | code |   |
| [`color_gamut`](https://whiskerjs.com/signals/color-gamut/) | ok | code |   |
| [`content_blockers`](https://whiskerjs.com/signals/content-blockers/) | ok | code | none, or the bait names a content blocker hid. |
| [`cpu_architecture`](https://whiskerjs.com/signals/cpu-architecture/) | ok | code |   |
| [`device_memory`](https://whiskerjs.com/signals/device-memory/) | unavailable | code | navigator.deviceMemory is a Chromium API. Reports unavailable / no_api. |
| [`dynamic_range`](https://whiskerjs.com/signals/dynamic-range/) | ok | code |   |
| [`font_preferences`](https://whiskerjs.com/signals/font-preferences/) | ok | code |   |
| [`fonts`](https://whiskerjs.com/signals/fonts/) | ok | code |   |
| [`fonts_canvas`](https://whiskerjs.com/signals/fonts-canvas/) | ok | code |   |
| [`forced_colors`](https://whiskerjs.com/signals/forced-colors/) | ok | code |   |
| [`hardware_concurrency`](https://whiskerjs.com/signals/hardware-concurrency/) | ok | code | Read 16 times; noisy / read_varies if the reads disagree. Some browsers cap or round the value. |
| [`heap_limit`](https://whiskerjs.com/signals/heap-limit/) | unavailable | code | performance.memory is a Chromium API. Reports unavailable / no_api. |
| [`intl_locale`](https://whiskerjs.com/signals/intl-locale/) | ok | code |   |
| [`keyboard_layout`](https://whiskerjs.com/signals/keyboard-layout/) | unavailable | code | No navigator.keyboard here: unavailable / no_api. |
| [`languages`](https://whiskerjs.com/signals/languages/) | ok | code |   |
| [`math`](https://whiskerjs.com/signals/math/) | ok | code |   |
| [`media_codecs`](https://whiskerjs.com/signals/media-codecs/) | ok | code |   |
| [`noise_profile`](https://whiskerjs.com/signals/noise-profile/) | ok | code | exact on stock Firefox; seeded for canvas or WebGL with Fingerprinting Protection or a noise extension. |
| [`pdf_viewer`](https://whiskerjs.com/signals/pdf-viewer/) | ok | code |   |
| [`permissions`](https://whiskerjs.com/signals/permissions/) | ok | code |   |
| [`pixel_ratio`](https://whiskerjs.com/signals/pixel-ratio/) | ok | code |   |
| [`platform`](https://whiskerjs.com/signals/platform/) | ok | code |   |
| [`plugins`](https://whiskerjs.com/signals/plugins/) | ok | code |   |
| [`prefers_contrast`](https://whiskerjs.com/signals/prefers-contrast/) | ok | code |   |
| [`private_click`](https://whiskerjs.com/signals/private-click/) | none | code | No attributionSourceId on anchors: reports the plain value none. |
| [`reduced_motion`](https://whiskerjs.com/signals/reduced-motion/) | ok | code |   |
| [`screen_frame`](https://whiskerjs.com/signals/screen-frame/) | varies | code | Excluded (firefox_fpp) when Fingerprinting Protection is on: in private windows, and in normal windows as Firefox 151 and later roll it out. Otherwise ok. |
| [`screen_resolution`](https://whiskerjs.com/signals/screen-resolution/) | ok | code |   |
| [`speech_voices`](https://whiskerjs.com/signals/speech-voices/) | ok | code | Digest of the voice list, or none; some browsers fill the list late. |
| [`storage`](https://whiskerjs.com/signals/storage/) | ok | code |   |
| [`time_zone`](https://whiskerjs.com/signals/time-zone/) | ok | code |   |
| [`touch_support`](https://whiskerjs.com/signals/touch-support/) | ok | code |   |
| [`ua_client_hints`](https://whiskerjs.com/signals/ua-client-hints/) | unavailable | code | navigator.userAgentData (Client Hints) is a Chromium API. Reports unavailable / no_api. |
| [`ua_versions`](https://whiskerjs.com/signals/ua-versions/) | unavailable | code | navigator.userAgentData (Client Hints) is a Chromium API. Reports unavailable / no_api. |
| [`vendor`](https://whiskerjs.com/signals/vendor/) | ok | code |   |
| [`vendor_flavors`](https://whiskerjs.com/signals/vendor-flavors/) | ok | code |   |
| [`webgl_image`](https://whiskerjs.com/signals/webgl-image/) | varies | code | Excluded (firefox_fpp) when Fingerprinting Protection is on: in private windows, and in normal windows as Firefox 151 and later roll it out. Otherwise ok. |
| [`webgl_params`](https://whiskerjs.com/signals/webgl-params/) | ok | code | Where WebGL 1 is available; unavailable / no_context without it. |
| [`webgl_renderer`](https://whiskerjs.com/signals/webgl-renderer/) | ok | code | Read from gl.RENDERER; WEBGL_debug_renderer_info is not requested in Firefox. |
| [`webgl_vendor`](https://whiskerjs.com/signals/webgl-vendor/) | ok | code | Read from gl.VENDOR; WEBGL_debug_renderer_info is not requested in Firefox. |
| [`webgpu`](https://whiskerjs.com/signals/webgpu/) | varies | code | Depends on browser version, operating system and GPU; needs a secure context. unavailable / no_api without navigator.gpu, no_context without an adapter. |

## Questions and answers

### Why does WhiskerJS not request WEBGL_debug_renderer_info in Firefox?

Firefox prints a deprecation warning in the console when that extension is requested, and its plain gl.RENDERER and gl.VENDOR values already carry the unmasked strings. WhiskerJS writes nothing to the console, so it reads the plain values.

### My Firefox runs in UTC. Will it be treated as resistFingerprinting?

Not on that alone. The firefox_rfp class needs every trait at once: an RFP time zone, a screen equal to the content area, no taskbar space, pixel ratio 1, a window at 0,0, and navigator.webdriver not true.

### Why did my Firefox get a new id in every new session before signalsVersion 9?

Firefox 155 randomises canvas toDataURL() per session while getImageData() stays exact. Until signalsVersion 8 the canvas component hashed toDataURL(), so stock Firefox gave a new id in every new browser context. Since signalsVersion 9 canvas reads the pixels with getImageData(), and the Firefox id is stable across sessions.

### What is the firefox_fpp class?

Firefox with Fingerprinting Protection: a Firefox that is not resistFingerprinting and whose canvas or WebGL known-answer probe came back altered. FPP is on in private windows and, since Firefox 151, is rolling out to normal windows. canvas, screen_frame and webgl_image are then excluded with ruleId firefox_fpp.

### Is there lab data for Firefox?

No. The early lab sample covered Mac Chrome, Mac Safari, iPhone Chrome and iPhone Safari only.

## Related pages

- [Firefox resistFingerprinting, Tor and Mullvad](https://whiskerjs.com/browsers/firefox-rfp-and-tor/): When Firefox normalises values on purpose.
- [noise_profile](https://whiskerjs.com/signals/noise-profile/): The known-answer probes that recognise Fingerprinting Protection.
- [webgl_renderer](https://whiskerjs.com/signals/webgl-renderer/): The GPU name from WebGL.
- [vendor](https://whiskerjs.com/signals/vendor/): Reported as empty in Firefox.
- [engine_mismatch](https://whiskerjs.com/docs/integrity/#engine_mismatch): Checks that a Firefox class behaves like Gecko.
- [Browser support matrix](https://whiskerjs.com/browsers/support-matrix/): All browsers side by side.
