Skip to main content
Check a launch in this order. Each step costs more than the one before it and gets closer to what a real detector sees.

1. Read the composed machine

--fingerprint-explain prints the machine a launch presents and exits without opening a window. Pass the same switches the launch uses:
Below the header, one row per value names the layer and the evidence it came from. Lines that start with ! explain a row, and the limitations list at the end names what the launch could not apply on this host. Quickstart shows the value rows, and Seeds and identity explains the seed lines. The explain output comes from the composition. It does not show what a page reads, and it does not know the locale and timezone a package would look up. The next step shows both.

2. Read what a page sees

what_a_page_sees.py launches a persona and serves it a page from a local HTTP server. The page reads its values, starts a worker that reads them again, fetches the request headers the browser sent, and posts everything back. The script prints the result as JSON.
Without --locale and --timezone, the package looks them up from your IP address. --headed uses the package’s Xvfb on a server. Output from a Debian 13 container on an arm64 host, apostate 0.4.3:
What to check in the output:
  • Page, worker and headers agree. The User-Agent, platform, cores, memory, languages, timezone and WebGL renderer in worker match the page’s. The user-agent and accept-language headers match navigator, and sec-ch-ua-platform matches userAgentData.platform.
  • Locale and timezone match the exit. Behind a proxy, intl and accept-language should belong to the proxy’s location. Locale and timezone covers the lookup.
  • The persona’s fonts are present. A Windows persona reads true for Segoe UI and Calibri and false for the macOS and Linux families. false for Segoe UI means the Windows fonts are not installed on the host. Fonts covers installing them.
  • webgpu is not null. On Linux it is null in headless launches and in headed launches as root. Linux servers compares the modes, and Known gaps tracks the gap.
  • architecture is the host’s. It reads arm here because the host is arm64, and x86 in an amd64 container. No real Windows machine pairs arm with this GPU, and iphey.com flags the pair. Hosts explains why.
  • available is smaller than size for a Windows persona, by the taskbar.
viewport is 889 high here, where a headless launch reads 945. The 0.4.3 package passes --no-sandbox, so this headed launch shows Chromium’s unsupported-flag bar on its first tab, which takes 56 pixels. From 0.4.4 the package keeps the sandbox on where it runs and leaves the flag out. Known gaps has the workaround. The script checks five font families by comparing text widths. document.fonts.check() returns true for every family name, installed or not, so it cannot tell.

3. Open public test pages

Public test pages read more values than the script, and some compare them with what real browsers report. Open them in a launch with the same persona, proxy and options you use for real work, because several of them also judge the IP address. WebRTC covers checking for WebRTC leaks.

4. Measure FingerprintJS Pro

scripts/measure-fpjs.py launches one persona headed through a proxy, loads the FingerprintJS Pro playground and prints the exit’s address, country and timezone, the suspect score and each flag behind it. It runs from a checkout of the repository and needs curl and a residential proxy. On a server it uses the package’s Xvfb.
Use a new proxy session for each run, or the service sees a returning visitor. --platform host measures the real machine for comparison, --out FILE saves the full response, and APOSTATE_BINARY points it at a local build. FingerprintJS has dated results.

Next steps

The test suite checks what pages read on a real browser, in every context, and opens public detector pages beside control browsers. It writes every result to a file.

Test suite

What the offline and live tiers check, and how to read the outcomes.

Latest results

The offline checks and public detector pages from the newest run on each host.