Browsers · WhiskerJS 0.1.0

Firefox resistFingerprinting, Tor Browser and Mullvad Browser

When Firefox reports fixed values on purpose, WhiskerJS leaves nine components out instead of reading normalised values, and it only does so when every trait is present.

In plain words

Firefox has a setting called resistFingerprinting, and Tor Browser and Mullvad Browser use it too. It makes the browser report the same, deliberately ordinary values as many other users: the time zone, the screen size and several graphics details. Reading those values would add nothing useful, so when WhiskerJS sees all the signs of this mode it simply does not collect nine of its measurements. It is careful to switch only when every sign is there, so an ordinary Firefox is not affected.

At a glance

  • Browser class: firefox_rfp, a special case of Firefox; rfp_hardened for Tor Browser and Mullvad Browser (since signalsVersion 6).
  • Excluded components: 9, with ruleId firefox_rfp or rfp_hardened (7 of them in the visitorId). Both rows list the same components.
  • Also unavailable by design, as in any Firefox: device_memory, heap_limit, ua_client_hints, ua_versions, keyboard_layout.
  • Lab sample: none.

The exact test

A Firefox (a Firefox/<number> user agent token or mozInnerScreenX) is classed firefox_rfp only when all of these hold, in this order, as written in src/browser-class.ts:

  1. The Intl time zone is UTC, Etc/UTC or Atlantic/Reykjavik (newer releases report the last).
  2. navigator.webdriver is not true.
  3. screen.width is a positive number equal to innerWidth, and screen.height equals innerHeight (the screen is the content area).
  4. screen.availWidth and availHeight equal the screen size (no space kept for a taskbar).
  5. devicePixelRatio is exactly 1.
  6. screenX and screenY are 0 (or absent).

If all six hold, the class is rfp_hardened when, in addition, the content area is on the letterboxing steps (innerWidth a positive multiple of 200 and innerHeight a positive multiple of 100 CSS pixels) and navigator.languages is en-US alone or en-US,en; otherwise firefox_rfp.

Otherwise it is plain firefox (or firefox_fpp when the canvas or WebGL known-answer probe came back altered; see Firefox). Each trait alone is common in stock Firefox, and stock Firefox under automation matches several of them (Playwright runs it in UTC with the screen equal to the viewport), so the test errs towards firefox. A missed resistFingerprinting browser only leaves normalised values in the result; a false positive would drop nine components from a normal browser.

The signalsVersion 5 fix

Before signalsVersion 5, stock Firefox under automation was wrongly classed firefox_rfp and lost nine components. The class now needs every trait at once, accepts Atlantic/Reykjavik as an RFP time zone, and never applies when navigator.webdriver is true. The id effect is limited to Firefox browsers that were wrongly classed before. See What’s new.

What works

Everything not in the exclusion row: offline audio, fonts and font preferences, WebGL parameters, Math, codecs, plugins, languages and Intl locale, color depth, touch support, storage presence, permission states and the display preference queries. Several of these will also be normalised by resistFingerprinting (for example the language), so expect them to be alike across users; WhiskerJS reports what the browser says.

What is unavailable or blocked

The Chromium-only components are unavailable / no_api as in any Firefox. audio_latency is skipped on desktop. canvas_readback and noise_profile are not excluded, but resistFingerprinting changes canvas readback, so altered or a probe state other than exact is likely; that is useful information in itself.

What WhiskerJS excludes and why

Component In visitorId What resistFingerprinting does to it
canvas yes blocks or alters canvas readback
hardware_concurrency yes reports a fixed core count
pixel_ratio no reports 1
screen_frame no available area equal to the screen
screen_resolution yes the content-area size as the screen
time_zone yes UTC (newer releases Atlantic/Reykjavik)
webgl_image yes blocks or alters readback
webgl_renderer yes placeholder strings
webgl_vendor yes placeholder strings

These report excluded with ruleId firefox_rfp, or rfp_hardened for Tor Browser and Mullvad Browser. With the WebGL renderer excluded, neither software_renderer nor vm_renderer can fire. In the ids they count as “missing”, the same in every run, so they do not make the id unstable.

Tor Browser and Mullvad Browser

Tor Browser and Mullvad Browser are built on Firefox and use resistFingerprinting, so they normally meet the test above. Since signalsVersion 6 they are classed rfp_hardened, recognised by two further measures they apply: letterboxing, which keeps the content area on steps of 200 by 100 CSS pixels, and a spoofed English locale (navigator.languages is en-US or en-US,en). A stock resistFingerprinting Firefox in English whose window happens to sit on those steps (its first window is rounded to them) is classed rfp_hardened too; the two rows exclude the same components, so only the label differs. The Tor Project describes further measures, such as shipping a limited set of fonts, all aimed at making users look the same. WhiskerJS does not try to get around any of it. In practice, many Tor Browser users of the same version will look alike, and sharing a visitorId is expected. For fraud and abuse work, the fact that a visitor uses Tor is usually better taken from your server’s own view (for example the network address) than from the fingerprint.

Integrity flags to expect

Early lab observations

There is no lab data for resistFingerprinting, Tor Browser or Mullvad Browser yet; the early, small sample at signalsVersion 1 covered Mac and iPhone Chrome and Safari only.

Testing it yourself

In Firefox, set privacy.resistFingerprinting to true in about:config, maximise or resize as your setup needs, and open the live demo. If browser_class says firefox_rfp (or rfp_hardened in Tor Browser or Mullvad Browser), the nine components above show excluded. If it says firefox, one of the traits did not hold; that is the safe outcome. The lab mode copies one line per run.

Support table for Firefox resistFingerprinting, Tor Browser and Mullvad Browser

Expected status of every component in this browser at signalsVersion 9, from the 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.

ComponentStatusBasisNote
apple_paynonecodeNo ApplePaySession: reports the plain value none.
audiookcodenoisy / seeded_noise if the audio known-answer probe sees noise.
audio_latencyvariescodeSkipped on desktop; read only with an Android user agent.
browser_classokcodeClass firefox_rfp only when every resistFingerprinting trait holds, rfp_hardened for Tor and Mullvad Browser (letterboxing and spoofed English); otherwise firefox.
canvasexcludedcodeExclusion table: firefox_rfp, or rfp_hardened in Tor and Mullvad Browser (fixed or normalised values).
canvas_readbackvariescoderesistFingerprinting changes canvas readback, so altered is likely.
color_depthokcode
color_gamutokcode
content_blockersokcodenone, or the bait names a content blocker hid.
cpu_architectureokcode
device_memoryunavailablecodenavigator.deviceMemory is a Chromium API. Reports unavailable / no_api.
dynamic_rangeokcode
font_preferencesokcode
fontsokcodeTor Browser limits fonts to a bundled set, so values are alike across its users.
fonts_canvasokcodeTor Browser limits fonts to a bundled set, so values are alike across its users.
forced_colorsokcode
hardware_concurrencyexcludedcodeExclusion table: firefox_rfp, or rfp_hardened in Tor and Mullvad Browser (fixed or normalised values).
heap_limitunavailablecodeperformance.memory is a Chromium API. Reports unavailable / no_api.
intl_localeokcode
keyboard_layoutunavailablecodeNo navigator.keyboard here: unavailable / no_api.
languagesokcoderesistFingerprinting can normalise the reported language.
mathokcode
media_codecsokcode
noise_profileokcodeProbe states other than exact are likely, since resistFingerprinting changes readback.
pdf_viewerokcode
permissionsokcode
pixel_ratioexcludedcodeExclusion table: firefox_rfp, or rfp_hardened in Tor and Mullvad Browser (fixed or normalised values).
platformokcode
pluginsokcode
prefers_contrastokcode
private_clicknonecodeNo attributionSourceId on anchors: reports the plain value none.
reduced_motionokcode
screen_frameexcludedcodeExclusion table: firefox_rfp, or rfp_hardened in Tor and Mullvad Browser (fixed or normalised values).
screen_resolutionexcludedcodeExclusion table: firefox_rfp, or rfp_hardened in Tor and Mullvad Browser (fixed or normalised values).
speech_voicesvariescodeDigest of the voice list, or none when the list is empty.
storageokcode
time_zoneexcludedcodeExclusion table: firefox_rfp, or rfp_hardened in Tor and Mullvad Browser (fixed or normalised values).
touch_supportokcode
ua_client_hintsunavailablecodenavigator.userAgentData (Client Hints) is a Chromium API. Reports unavailable / no_api.
ua_versionsunavailablecodenavigator.userAgentData (Client Hints) is a Chromium API. Reports unavailable / no_api.
vendorokcode
vendor_flavorsokcode
webgl_imageexcludedcodeExclusion table: firefox_rfp, or rfp_hardened in Tor and Mullvad Browser (fixed or normalised values).
webgl_paramsokcodeWhere WebGL 1 is available; unavailable / no_context without it.
webgl_rendererexcludedcodeExclusion table: firefox_rfp, or rfp_hardened in Tor and Mullvad Browser (fixed or normalised values). Neither renderer flag can fire.
webgl_vendorexcludedcodeExclusion table: firefox_rfp, or rfp_hardened in Tor and Mullvad Browser (fixed or normalised values).
webgpuvariescodeDepends 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

How does WhiskerJS know resistFingerprinting is on?

It does not read the setting. It classes Firefox as firefox_rfp when every trait the setting produces holds at once: an RFP time zone (UTC, Etc/UTC or Atlantic/Reykjavik), a screen equal to the content area, no taskbar space, a pixel ratio of 1, a window at 0,0, and navigator.webdriver not true.

How are Tor Browser and Mullvad Browser told apart from plain resistFingerprinting?

They run resistFingerprinting with letterboxing and a spoofed English locale. When every resistFingerprinting trait holds and, on top, the content area sits on the letterboxing steps (width a multiple of 200, height a multiple of 100 CSS pixels) and navigator.languages is en-US or en-US,en, the class is rfp_hardened (since signalsVersion 6). Both classes exclude the same nine components.

Will Tor Browser users get the same visitorId?

Many may. Tor Browser is designed to make its users look alike, and WhiskerJS respects that: the normalised components are left out, and the rest tend to be similar across users of the same Tor Browser version. Treat a shared id here as expected.

Can a stock Firefox be wrongly classed firefox_rfp?

The test is built to avoid it, and since signalsVersion 5 it never applies when navigator.webdriver is true. A wrong class would drop nine components; a missed one only leaves normalised values in, so the test errs towards plain firefox.