How browser fingerprinting works, signal by signal
Browser fingerprinting reads network and in-page signals; mismatched values like Windows User-Agent with Linux fonts get flagged. Learn to audit your browser.
- browser-fingerprinting
- canvas
- webgl
- tls
- anti-detect
On this page
- What is browser fingerprinting and how does it work?
- Which network-level signals identify a browser?
- Which in-page signals does browser fingerprinting read?
- Does rarity or consistency matter more in browser fingerprinting?
- Why do noisy or spoofed fingerprints get flagged?
- How do you audit a browser with AmIUnique, Cover Your Tracks and CreepJS?
- Where does Wayfern fit in browser fingerprinting?
- Frequently asked questions
- Does incognito mode or a VPN stop browser fingerprinting?
- Is a more unique fingerprint always easier to detect?
- Why does my headless browser get flagged even with a normal User-Agent?
- Is browser fingerprinting the same as cookies?
- What should you do next?
- Sources
Browser fingerprinting reads two layers: network-level signals (IP, TLS, HTTP headers) and in-page signals (canvas, WebGL, audio, fonts, navigator, screen, timezone). Detectors check whether a value is rare, and they also check that the signals agree with each other. When an automated browser gets flagged, one mismatched layer can be enough to give it away.
A Playwright or Puppeteer script passes on your laptop, then meets a challenge page in production. The cause is rarely one value. Still, one value is readable in a single line: MDN says navigator.webdriver indicates whether the user agent is controlled by automation, and the W3C WebDriver spec defines it.
You will be able to map each signal to the layer that exposes it, see how detectors cross-check those layers, and audit your own browser.
Key Takeaways
- Uniqueness percentages depend on who was measured and how, so the studies disagree. Quote none as a constant.
- A value that changes on every read is a signal in itself.
- AmIUnique, Cover Your Tracks and CreepJS show what your browser exposes. None tells you how a given detector scores it.
What is browser fingerprinting and how does it work?
Browser fingerprinting identifies a browser by reading its properties instead of storing an identifier. A script or server collects HTTP headers, JavaScript API values, rendering output from canvas, WebGL and audio, and network details. It combines them into a profile that recognizes the same browser again without cookies.

Wikipedia’s device fingerprint article defines a device fingerprint as information collected about a remote device’s software and hardware, “usually assimilated into a brief identifier using a fingerprinting algorithm.”
Collection is stateless. A cookie is a value a site writes to your browser and reads back later. A fingerprint writes nothing: the site reads attributes the browser already exposes, so clearing storage doesn’t remove it.
On each visit, the system hashes the attribute vector or compares it against stored ones. A match, or a close one, links the visits.
Wikipedia’s article also covers “Diversity and stability”: a useful fingerprint differs between users and stays put over time. A value shared by every Chrome install has no diversity. A value that changes every minute has no stability.
Defenders use the same vector. A fraud team can score whether a login comes from a browser the account has used before. A bot filter can check whether attributes contradict each other, such as a Windows User-Agent paired with Linux rendering output.
A tracker uses the identical mechanism to follow you across sites.
Which network-level signals identify a browser?
Network-level signals are the values a server reads from the connection and the request, before any JavaScript runs or entirely outside the page. Each has an in-page twin, and a detector looks for the two disagreeing.
TLS ClientHello. The handshake announces the cipher suites, extensions and curves your TLS stack supports. The JA3 repository describes JA3 as “a method for creating SSL/TLS client fingerprints that should be easy to produce on any platform and can be easily shared for threat intelligence.”
The handshake must agree with navigator.userAgent. A Chromium user agent arriving over a non-Chromium TLS stack, such as a default HTTP library, is a mismatch no page script can fix.
User-Agent header. The User-Agent request header and navigator.userAgent should return the same string. If you override one in your automation and not the other, the server sees both.
User-Agent Client Hints. MDN’s Sec-CH-UA reference says the Sec-CH-UA header “provides the user-agent’s branding and significant version information.” Compare it with navigator.userAgentData.brands, and compare Sec-CH-UA-Platform with the platform in the user agent string. A Windows string paired with a Linux platform hint is a contradiction.
Header values and order. RFC 9110 states that “the order in which field lines with differing field names are received in a message is not significant.” Because the protocol doesn’t care, each client picks its own order and its own set of headers, including Accept-Encoding and the Sec-Fetch-* family.
Vendors don’t publish their exact ordering rules. Test your client against a real browser instead of assuming a rule.
Accept-Language. The header tells the server which natural languages and locales the client prefers. It should line up with navigator.languages, and both should look plausible for the IP’s country.
IP reputation and ASN type. The server looks up the address in reputation lists and checks whether the network belongs to a datacenter or a residential provider. It also maps the IP to a country and city.
That location must fit the in-page timezone from Intl.DateTimeFormat().resolvedOptions().timeZone and the language list above.
Across all six, the network layer is a second witness for values the page reports about itself. Fixing one side only moves the contradiction. For exit and location choices, see our guide to anti-detect browser sessions over CDP.
Which in-page signals does browser fingerprinting read?
Browser fingerprinting reads seven in-page surfaces: canvas, WebGL, audio, fonts, navigator and hardware values, screen and window metrics, and timezone with locale. GeeksforGeeks describes the process as “analyzing data like screen resolution, installed fonts, and timezone”. Each surface is only useful to a detector when it agrees with the others.
| Signal | API | Must match |
|---|---|---|
| Canvas | toDataURL(), getImageData() | WebGL renderer, claimed OS |
| WebGL | WEBGL_debug_renderer_info | Claimed OS and device class |
| Audio | OfflineAudioContext | Browser build, OS |
| Fonts | Text width and height measurement | Claimed platform |
| Hardware | navigator.* | User-Agent, headers, touch |
| Screen and window | screen.*, innerWidth | Claimed device, viewport |
| Timezone, locale | Intl.DateTimeFormat() | IP location, Accept-Language |
Canvas. A script draws text and shapes, then reads the pixels back. MDN’s toDataURL() reference says toDataURL() “returns a data URL containing a representation of the image in the format specified by the type parameter”. The output differs by GPU, driver, font rasterization and OS.
WebGL. The WEBGL_debug_renderer_info extension, MDN says, “exposes two constants with information about the graphics driver for debugging purposes”: UNMASKED_VENDOR_WEBGL and UNMASKED_RENDERER_WEBGL. Scripts also read getParameter() limits and the extension list. The renderer string has to fit the OS and device class you claim.
Audio. A script builds an oscillator and a compressor in an OfflineAudioContext, renders the samples and hashes them. MDN’s OfflineAudioContext page notes that this context “doesn’t render the audio to the device hardware”, so nothing plays. Small differences in the audio stack change the hash.
Fonts. A script measures text width and height against a fallback font. A different size means the tested font is installed. Font sets differ by OS, so the list ties you to a platform.
Navigator and hardware. MDN’s hardwareConcurrency reference says hardwareConcurrency “returns the number of logical processors available to run threads on the user’s computer”. MDN’s deviceMemory page describes deviceMemory as returning “the approximate amount of device memory in gigabytes”, so that value is coarse and capped.
Check platform, languages and maxTouchPoints against the User-Agent and your screen type.
Screen and window. Scripts read screen.width, screen.height, availWidth, devicePixelRatio, color depth, and innerWidth against outerWidth. Headless and emulated windows can show telltale relations, such as an outer window that exactly equals the inner one. The screen should fit the claimed device.
Timezone and locale. Intl.DateTimeFormat().resolvedOptions().timeZone returns the zone, and the same call returns the locale. A detector compares both with the IP’s location and the Accept-Language header. A US exit with a Singapore timezone stands out.
If you drive a hosted browser, read each of these values from your own page first. Our page on anti-detect browser sessions covers the connection side.
Does rarity or consistency matter more in browser fingerprinting?
Rarity is what a tracker wants, but consistency is what a bot or fraud detector checks. A rare fingerprint whose values could exist together on one real machine passes, and a common one with contradictions fails.
Start with rarity. Three studies measured how many browsers were unique, and they disagree:
- Eckersley, Panopticlick, 2010: 83.6% of 470,161 fingerprints were unique (How Unique Is Your Web Browser?).
- Laperdrix et al., AmIUnique, 2016: 89.4% of fingerprints were unique (Wikipedia’s summary), in a sample of 118,934 (Beauty and the Beast).
- Gomez-Boix et al., “Hiding in the Crowd,” 2018: 33.6% of 2,067,942 fingerprints were unique (Hiding in the Crowd paper).
The gap has three likely causes. The first two samples came from people who chose to test their own browser, which probably skews toward privacy-minded users with unusual setups. The 2018 sample came from one of the top 15 French websites.
The studies also measured different attribute sets, and browsers changed over eight years. All three figures are dated.
Coherence works differently. A detector doesn’t need your fingerprint to be common. It asks whether the values fit together:
- The
User-Agentsays Windows, butnavigator.platformreports a Mac. - The WebGL renderer names a GPU that doesn’t ship in the claimed OS or device class.
- The font list lacks the system fonts the claimed OS installs by default.
- The browser timezone sits an ocean away from the IP’s location.
Vendors don’t publish their detection rules, so read this list as mechanisms. No single mismatch is known to trigger a block.
Rarity alone costs you nothing, because real machines are rare too. A contradiction between two layers is what stands out, so compare the network layer against the in-page layer before you change any single value.
Why do noisy or spoofed fingerprints get flagged?
Noisy or spoofed fingerprints get flagged because the noise, or a mismatch between values, makes a browser stand out from real ones. Hardening can raise rarity instead of lowering it. Random noise breaks stability, and impossible combinations break plausibility.
Vastel et al. studied this in “The Privacy Implications of Browser Fingerprint Inconsistencies” (the FP-Scanner paper, USENIX Security 2018). Their work treats inconsistencies as something a script can detect.
| Failure pattern | Why it is detectable |
|---|---|
| Windows User-Agent with macOS fonts | The font list implies a different operating system |
| Mobile screen size with a desktop GPU renderer | No real phone reports that WebGL renderer |
| Canvas output that changes on every read | Real hardware renders the same image twice |
| Timezone that disagrees with the IP location | The network and the page report different places |
| Chromium User-Agent with a non-Chromium TLS fingerprint | The header claims one engine, the handshake shows another |
Defenses split into two strategies. Homogenization makes many browsers look the same, and the Tor Browser design document has a section titled “Randomization versus Uniformity”.
Randomization adds noise, as Brave’s fingerprint randomization does. It suits privacy but not session consistency, because the value isn’t stable.
Brave seeds small random changes to canvas, WebGL and audio output per session and per site. That helps a person avoid cross-site tracking. A long-running automated session needs the same values on every visit, so you want the opposite. Our comparison of anti-detect browsers covers that trade-off.
How do you audit a browser with AmIUnique, Cover Your Tracks and CreepJS?
You audit a browser by running AmIUnique, Cover Your Tracks and CreepJS in the exact browser and exit you use in production, then tracing each odd value back to the signal that collected it.
- Run from production. Use the same browser build, headless mode, viewport and exit IP as your job. A laptop result tells you nothing about a datacenter exit.
- Read AmIUnique by rarity. Each attribute carries a similarity ratio, the share of visitors with the same value. Start with the lowest ratios and map each to a signal: canvas and WebGL hashes to rendering, the font list to enumeration, language and encoding to HTTP headers.
- Read Cover Your Tracks for scale. The EFF describes Cover Your Tracks as a tool to “see how trackers view your browser”. Note the bits of identifying information and the “one in N browsers” line.
- Interpret that figure narrowly. A high figure means your values are rare in that test’s sample. It doesn’t prove a given site tracks you, and a low figure doesn’t prove you pass a bot check.
- Use CreepJS for contradictions. Don’t chase a score. Read the sections on lies, headless indicators and noise, and check their current names on the live page and its repository.
- Run everything twice. A hash that changes between loads points to noise injection.
- Compare headless with headed. Then check that the timezone, language and renderer agree with the IP and the User-Agent.
import { chromium } from "playwright";
const pages = {
amiunique: "https://amiunique.org/",
eff: "https://coveryourtracks.eff.org/",
creepjs: "https://abrahamjuliot.github.io/creepjs/",
};
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
for (const [name, url] of Object.entries(pages)) {
await page.goto(url, { waitUntil: "networkidle" });
await page.waitForTimeout(15000); // tests run after load
await page.screenshot({ path: `${name}.png`, fullPage: true });
}
await browser.close(); For a hosted browser, swap the launch line for chromium.connectOverCDP("wss://api.wayfern.com/ws?token=LINK_TOKEN"), as in the developer quickstart. Click through Cover Your Tracks by hand if its test needs a button press.
Run the snippet with plain Playwright Chromium and expect CreepJS to show headless and inconsistency findings. Each one traces back to a signal covered above, such as navigator.webdriver, a software renderer string or a screen size that doesn’t match the window.
Where does Wayfern fit in browser fingerprinting?
Wayfern is a hosted anti-detect Chromium that you drive over CDP, and it applies the fingerprint inside its own browser build. A Wayfern CDP domain generates and pins a per-target Windows, macOS or Linux identity. The browser presents a desktop device.
You set the viewport with width and height on the /ws URL. The real window is resized and nothing is emulated.
The honest catch, as our Wayfern vs Multilogin comparison lays out: no per-vector fingerprint switches for customers, no persistent named profiles, no mobile emulation, and no engine other than Chromium.
If a person hand-manages many identities, buy a profile manager. Multilogin is the better fit there.
If code is the operator, try an anti-detect browser session over CDP: the connection string above is all the developer quickstart needs.
Frequently asked questions
Does incognito mode or a VPN stop browser fingerprinting?
Neither one stops it. A VPN changes your IP address, not those values. One 2026 review describes ads still following users who cleared their cache and used private browsing.
Is a more unique fingerprint always easier to detect?
Not always. Rarity helps a tracker, but a bot detector checks coherence: whether the values could exist together on one real machine. A rare, consistent browser can pass, and a common, contradictory one fails.
Why does my headless browser get flagged even with a normal User-Agent?
The User-Agent is one value among many. A detector compares it with navigator properties, the TLS ClientHello and rendering output, then flags any mismatch. Fix the disagreement, not the header. For a hosted browser you drive over CDP, see our anti-detect browser sessions page.
Is browser fingerprinting the same as cookies?
No, a cookie is an identifier stored on your device that you can delete. Fingerprinting stores nothing. GeeksforGeeks says it works silently in the background by analyzing data like screen resolution, installed fonts and timezone.
What should you do next?
Today: Run AmIUnique, Cover Your Tracks and CreepJS from your production browser and exit. Write down every attribute that disagrees with the User-Agent, IP or timezone.
This week: Fix those contradictions before you add any noise. Then re-run all three tests twice and check that the values stay stable between runs.
Next: If you need a hosted Chromium that applies its fingerprint inside the browser, start with the Wayfern docs quickstart and read the pricing before you commit. We have no free tier, so plan for a paid start. If you need named profiles or per-vector switches, a desktop profile manager fits the job better.
Measure your own browser before you buy anything.
Sources
What is Browser Fingerprinting? - GeeksforGeeks · Navigator: webdriver property - Web APIs | MDN · WebDriver (W3C) · Cover Your Tracks · Device fingerprint - Wikipedia · Hiding in the Crowd: an Analysis of the Effectiveness of… · Anti-Detect Browser Sessions over CDP | Wayfern · Wayfern vs Multilogin: anti-detect browser comparison. Every other source is linked where the article cites it.
See us more often in Google
One click marks Wayfern as a preferred source in Google, so our posts can show up more often in your Top Stories, AI Mode and AI Overviews.