Browser Fingerprinting 2026: What Works, What Doesn't
A technical audit of browser fingerprinting in 2026. Which techniques survive Privacy Sandbox, anti-fingerprinting browsers, and stealth automation.
WebDecoy Team
WebDecoy Security Team
Browser fingerprinting has a reputation problem. Privacy advocates treat it as surveillance. Security engineers treat it as a detection tool. Both are right, and the tension between those perspectives has shaped every browser vendor decision for the past five years.
The result is a landscape that looks nothing like 2020. Chrome’s Privacy Sandbox has frozen or removed half the signals fingerprinting libraries depended on. Firefox and Safari have added noise injection and API restrictions. Brave blocks fingerprinting attempts outright. Meanwhile, headless browser frameworks have gotten dramatically better at spoofing the signals that remain.
If you’re building or maintaining a fingerprinting system in 2026, a lot of the advice from even two years ago is obsolete. This is a technical audit of what still produces usable signal, what’s been neutralized, and what’s emerged to replace the losses.
The Scoreboard
Before the details, here’s the summary. Each technique is rated on two axes: entropy (how much identifying information it produces) and durability (how resistant it is to spoofing and browser mitigation).
| Technique | Entropy | Durability | Status in 2026 |
|---|---|---|---|
| User-Agent string | Very low | None | Dead. Frozen by Chrome 107+. |
| navigator.plugins | None | None | Dead. Returns empty array in Chrome. |
| navigator.platform | Very low | None | Dead. Frozen to generic values. |
| Canvas fingerprint | Medium | Medium | Degraded but usable. Noise injection in Firefox/Brave. |
| WebGL fingerprint | Medium-High | Medium-High | Still strong. Renderer strings remain diverse. |
| WebGL rendering | Medium | Medium | Works. GPU-dependent output is hard to standardize. |
| AudioContext | Medium | Medium | Works. Subtle hardware-dependent timing differences persist. |
| Font enumeration | Low-Medium | Low | Declining. OS font standardization reduces entropy. |
| Screen/display props | Low | Low | Minimal entropy. Heavily spoofed by automation tools. |
| Client Hints | Low | Low | Reduced by design. Chrome limiting high-entropy hints. |
| TLS fingerprint (JA4) | High | Very High | Strongest signal. Cannot be spoofed from JS. |
| HTTP/2 settings | Medium | High | Underutilized. Connection-level signal with good entropy. |
| TCP/IP stack | Low-Medium | High | Niche but durable. OS-level differences are hard to fake. |
The trend is clear: JavaScript-accessible signals are eroding. Network-level signals are ascendant. The most durable fingerprinting techniques in 2026 operate below the browser’s API surface, where privacy extensions and stealth plugins can’t reach them.
What’s Dead
User-Agent String
The User-Agent string was the original fingerprinting signal. For two decades, browsers sent increasingly detailed strings identifying the browser name, version, operating system, device type, and rendering engine.
Chrome killed it. Starting with Chrome 107 in late 2022, Chrome began freezing the User-Agent string to a reduced format:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36The version number still increments, but the OS version is frozen to Windows NT 10.0, platform details are generic, and the string no longer differentiates between minor versions or OS builds. Firefox and Safari followed with similar reductions.
As a fingerprinting signal, the User-Agent string now provides almost zero entropy across modern browsers. You can distinguish Chrome from Firefox from Safari, and that’s about it. For bot detection, it’s even more useless because every automation framework trivially sets whatever User-Agent string it wants.
Verdict: Completely dead for fingerprinting. Retained only for legacy compatibility.
navigator.plugins and navigator.mimeTypes
These APIs used to return arrays of installed browser plugins (Flash, Java, PDF viewer, Silverlight) and supported MIME types. The combination was surprisingly unique. A user with Flash 32.0.0.453, Java 8u281, and Chrome PDF Viewer had a distinct signature that changed slowly over time.
Chrome removed meaningful data from both APIs as part of the Privacy Sandbox rollout. navigator.plugins now returns a fixed, generic array. navigator.mimeTypes returns a matching fixed array. Firefox has done the same. The plugin era is over, and these APIs went with it.
Verdict: Returns static values across all modern browsers. Zero entropy. Remove from any fingerprinting library that still checks these.
navigator.platform
navigator.platform used to distinguish between Windows, macOS, Linux, ChromeOS, and various mobile platforms with specific architecture details. Chrome has frozen it as part of the User-Agent reduction effort. It now returns generic values like Win32 regardless of actual architecture.
The replacement, navigator.userAgentData.platform, provides the same information through the Client Hints API but is designed to be lower entropy and requires a permission-gated request for detailed values.
Verdict: Frozen. The Client Hints replacement is intentionally low-entropy.
What’s Degraded but Usable
Canvas Fingerprinting
Canvas fingerprinting works by drawing a complex scene using the HTML5 Canvas API and reading back the pixel data. Differences in GPU hardware, driver versions, font rendering, and anti-aliasing produce slightly different outputs across devices. The technique generates a hash of the rendered image that serves as a partial fingerprint.
In 2026, canvas fingerprinting still works on most browsers, but its reliability has decreased:
Chrome: Still produces consistent, device-specific output. No noise injection. The highest-fidelity canvas fingerprinting target.
Firefox: Introduced canvas noise injection in Firefox 113 (the privacy.resistFingerprinting option) that adds random perturbation to canvas readback. Disabled by default but enabled in strict privacy mode and by default in Firefox private windows. When enabled, the same device produces different canvas hashes on every page load.
Safari: Minimal canvas protection. Still produces stable fingerprints. Apple has focused anti-fingerprinting efforts on other APIs.
Brave: Aggressively randomizes canvas output by default. Canvas fingerprinting is effectively useless against Brave users.
The practical impact: canvas fingerprinting still contributes meaningful entropy for Chrome and Safari users (roughly 80% of the market). For Firefox strict-mode and Brave users, it’s noise.
// Basic canvas fingerprint
function getCanvasFingerprint() {
const canvas = document.createElement('canvas');
canvas.width = 256;
canvas.height = 256;
const ctx = canvas.getContext('2d');
// Draw complex scene with text, gradients, curves
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillStyle = '#f60';
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = '#069';
ctx.fillText('Browser fingerprint', 2, 15);
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
ctx.fillText('Browser fingerprint', 4, 17);
// Add geometric shapes for GPU-dependent rendering
ctx.beginPath();
ctx.arc(50, 50, 50, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
return canvas.toDataURL();
}Verdict: Still works on 80% of browsers. Expect continued degradation as more browsers add noise injection.
Font Enumeration
Font fingerprinting measures which fonts are installed by rendering text in specific typefaces and detecting whether the browser fell back to a default. The combination of available fonts was once highly identifying because users install different font sets based on their applications (designers have Adobe fonts, developers have monospace collections, etc.).
The technique has weakened for two reasons:
OS standardization: Windows 11, macOS Sonoma/Sequoia, and modern Linux distros ship with increasingly similar default font sets. The baseline entropy has decreased.
Web font dominance: Sites that use web fonts (most of the modern web) don’t trigger system font rendering, reducing opportunities to probe the local font list.
Browser restrictions: Firefox’s
privacy.resistFingerprintingreturns a fixed font list. Safari restricts font enumeration in certain contexts.
Font fingerprinting still produces some entropy, especially distinguishing Windows from macOS from Linux, but the days of font lists as a high-entropy identifier are over.
Verdict: Low entropy. Useful as a supporting signal, not a primary identifier.
Screen and Display Properties
screen.width, screen.height, screen.colorDepth, window.devicePixelRatio, and related properties still work and still vary across devices. But the entropy is minimal because the market has consolidated around a handful of common resolutions and display configurations.
A 1920x1080 display at 1x pixel ratio describes tens of millions of devices. A 2560x1440 at 1.25x narrows it down, but not by much. These properties are also trivially spoofed by automation frameworks. Playwright and Puppeteer set arbitrary viewport sizes as a one-line configuration.
Verdict: Low entropy. Easy to spoof. Include in a composite fingerprint but don’t rely on it.
What Still Works
WebGL Fingerprinting
WebGL fingerprinting is the quiet workhorse of modern fingerprinting. It operates on two levels:
Level 1: Parameter enumeration. The WebGL API exposes hardware and driver information through getParameter() and getExtension() calls. The renderer string (WEBGL_debug_renderer_info) reveals the GPU model and driver:
function getWebGLFingerprint() {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
if (!gl) return null;
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
return {
vendor: gl.getParameter(gl.VENDOR),
renderer: gl.getParameter(gl.RENDERER),
unmaskedVendor: debugInfo
? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL)
: null,
unmaskedRenderer: debugInfo
? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL)
: null,
maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
maxViewportDims: gl.getParameter(gl.MAX_VIEWPORT_DIMS),
extensions: gl.getSupportedExtensions(),
shadingLanguageVersion: gl.getParameter(gl.SHADING_LANGUAGE_VERSION),
};
}The unmasked renderer string alone provides substantial entropy. ANGLE (NVIDIA GeForce RTX 4070 Ti Direct3D11 vs_5_0 ps_5_0) identifies a specific GPU model. Combined with max texture size, supported extensions, and shader precision formats, WebGL creates a detailed hardware profile.
Level 2: Render output. Drawing a complex 3D scene with WebGL and reading back the pixels produces GPU-dependent output, similar to canvas fingerprinting but with higher variability because 3D rendering pipelines differ more across GPU architectures than 2D rendering.
Why it’s durable: Browser vendors have been reluctant to restrict WebGL because doing so breaks legitimate web applications. Games, data visualizations, 3D product viewers, and mapping applications depend on accurate WebGL behavior. Injecting noise into WebGL output would break these use cases visibly.
Chrome does not restrict WEBGL_debug_renderer_info. Firefox hides it behind privacy.resistFingerprinting but keeps it available by default. Safari exposes it. Brave blocks it.
For bot detection specifically, WebGL is gold. Headless Chrome running in a cloud VM reports the VM’s virtual GPU, typically Google SwiftShader or llvmpipe, which is instantly distinguishable from any real user GPU. Even Browser-as-a-Service platforms that try to spoof this value can’t easily replicate the full constellation of WebGL parameters that a real GPU produces.
Verdict: High entropy. Durable. The best JavaScript-level fingerprinting signal available in 2026.
AudioContext Fingerprinting
AudioContext fingerprinting exploits hardware-dependent differences in how devices process audio signals. By creating an OscillatorNode, routing it through a DynamicsCompressorNode, and reading the output, you get floating-point audio sample values that vary based on the audio hardware and driver stack.
function getAudioFingerprint() {
return new Promise((resolve) => {
const context = new OfflineAudioContext(1, 44100, 44100);
const oscillator = context.createOscillator();
oscillator.type = 'triangle';
oscillator.frequency.setValueAtTime(10000, context.currentTime);
const compressor = context.createDynamicsCompressor();
compressor.threshold.setValueAtTime(-50, context.currentTime);
compressor.knee.setValueAtTime(40, context.currentTime);
compressor.ratio.setValueAtTime(12, context.currentTime);
compressor.attack.setValueAtTime(0, context.currentTime);
compressor.release.setValueAtTime(0.25, context.currentTime);
oscillator.connect(compressor);
compressor.connect(context.destination);
oscillator.start(0);
context.startRendering().then((buffer) => {
const data = buffer.getChannelData(0);
// Sum a slice of samples as the fingerprint
let sum = 0;
for (let i = 4500; i < 5000; i++) {
sum += Math.abs(data[i]);
}
resolve(sum);
});
});
}The output differs across operating systems, audio drivers, and hardware configurations. It’s not as high-entropy as WebGL, but it provides an independent signal that’s difficult to spoof because it depends on the actual audio processing pipeline, not just a JavaScript property.
Firefox adds noise when privacy.resistFingerprinting is enabled. Chrome and Safari return unmodified output. Brave blocks the technique.
For bot detection, AudioContext is useful because headless browsers often have no audio stack at all, or use a software implementation that produces distinctly different output from real hardware. A headless Chrome session on a Linux server produces AudioContext values that don’t match any real desktop configuration.
Verdict: Medium entropy. Good independence from visual fingerprints. Durable against casual spoofing.
TLS Fingerprinting (JA4)
This is the biggest shift in fingerprinting since canvas was discovered. TLS fingerprinting doesn’t operate in JavaScript at all. It analyzes the TLS ClientHello message sent during the HTTPS handshake, before any page content loads, before any JavaScript executes, before any browser API can be manipulated.
The ClientHello contains:
- Supported cipher suites in preference order
- TLS extensions and their order
- Supported groups (elliptic curves)
- Signature algorithms
- ALPN protocols
JA4 (the successor to JA3) hashes these values into a fingerprint that identifies the TLS stack implementation. Different browsers, different browser versions, and different HTTP client libraries produce different JA4 hashes. And critically, you cannot change your JA4 fingerprint from JavaScript. It’s determined by the TLS library compiled into the client.
This means:
- A Playwright bot claiming to be Chrome 126 but running on an older Chromium build has a JA4 fingerprint that doesn’t match real Chrome 126
- A Python
requestssession spoofing a Chrome User-Agent has a JA4 fingerprint that matches Python’surllib3, not Chrome - A Browser-as-a-Service platform running headless Chrome in the cloud has a JA4 fingerprint matching their specific Chromium build, which may be months behind the current stable release
TLS fingerprinting requires server-side or proxy-level access to the raw TLS handshake. You can’t do it from JavaScript. But if you control the server, or use a CDN that exposes TLS metadata (Cloudflare exposes JA3 and JA4 in firewall rules), it’s the most powerful identification signal available.
For a deep dive on JA4 implementation and real-world detection of AI scrapers, see our JA4 fingerprinting guide.
Verdict: High entropy. Extremely difficult to spoof. The single best fingerprinting signal in 2026, but requires server-side access.
HTTP/2 Settings Fingerprinting
When a client establishes an HTTP/2 connection, it sends a SETTINGS frame containing parameters like initial window size, max concurrent streams, header table size, and enabled push. Different HTTP client implementations choose different defaults.
Like TLS fingerprinting, HTTP/2 settings are determined by the client implementation, not configurable from JavaScript. A browser and a bot using the same TLS library but different HTTP/2 stacks will have different SETTINGS fingerprints.
This technique is less widely deployed than JA4 but provides an independent network-level signal. Combined with TLS fingerprinting, it creates a two-layer network fingerprint that’s extremely difficult to spoof without using the exact same network stack as the browser you’re impersonating.
Verdict: Medium entropy. High durability. Underutilized and worth adding to network-level detection stacks.
The Anti-Fingerprinting Landscape
Understanding what the other side is doing matters. Here’s the current state of anti-fingerprinting efforts, both from browsers protecting privacy and from automation tools evading detection.
Browser-Level Protections
Chrome (Privacy Sandbox): Google’s approach is surgical. Rather than blocking fingerprinting APIs, they’re reducing entropy from each one. Frozen User-Agent, reduced Client Hints, deprecated navigator.plugins. WebGL renderer info remains exposed because removing it would break too many sites. Chrome’s goal is to make fingerprinting less unique, not impossible.
Firefox (Enhanced Tracking Protection + resistFingerprinting): Firefox offers the most granular anti-fingerprinting controls. The privacy.resistFingerprinting flag (disabled by default, enabled in Tor Browser) adds noise to canvas, restricts font enumeration, normalizes screen dimensions, and limits timer precision. Standard Firefox with Enhanced Tracking Protection blocks known fingerprinting scripts by domain but doesn’t modify API output.
Safari (Intelligent Tracking Prevention): Safari takes a pragmatic approach. It restricts some fingerprinting vectors (limiting document.fonts access, reducing timer precision) but focuses primarily on cookie and storage partitioning. Safari’s fingerprinting surface is larger than Firefox’s strict mode but smaller than Chrome’s.
Brave: The most aggressive anti-fingerprinting browser. Randomizes canvas output, blocks WebGL renderer info, adds noise to AudioContext, limits font enumeration, and blocks known fingerprinting scripts. Brave’s goal is to make fingerprinting actively unreliable, not just lower entropy.
Automation Tool Countermeasures
Puppeteer Extra Stealth Plugin: Patches navigator.webdriver, spoofs plugins arrays, overrides Chrome runtime properties, and modifies other detectable automation artifacts. Does not affect TLS fingerprints or network-level signals.
Playwright Stealth Patches: Similar to Puppeteer Stealth. Masks automation-specific JavaScript properties. Increasingly includes WebGL spoofing that overrides WEBGL_debug_renderer_info to report a realistic GPU string instead of SwiftShader.
Browser-as-a-Service (Browserbase, Hyperbrowser): These platforms run real Chromium instances in the cloud, producing genuine browser fingerprints at the JavaScript level. Their weakness is TLS fingerprinting. The Chromium version they run often lags behind the current stable release, creating a detectable mismatch between the claimed User-Agent version and the actual TLS fingerprint.
Anti-detect Browsers (Multilogin, GoLogin, Dolphin Anty): Commercial tools designed specifically to defeat fingerprinting. They generate customizable browser profiles with unique canvas, WebGL, AudioContext, and font fingerprints. These are the hardest to detect because they use modified real browser engines rather than automation frameworks. Detection requires ensemble methods because no single signal catches them.
Building a Fingerprinting System in 2026
If you’re building fingerprinting into a product today, here’s the architecture that accounts for the current reality.
Layer 1: Network Fingerprints (Server-Side)
Collect at the proxy or load balancer level:
TLS ClientHello → JA4 hash
HTTP/2 SETTINGS → settings fingerprint
TCP/IP characteristics → OS fingerprintThese signals are the foundation because they cannot be manipulated from the client. They tell you what the client actually is, regardless of what it claims to be. A mismatch between the JA4 fingerprint and the claimed User-Agent is an immediate red flag.
Layer 2: Hardware Fingerprints (JavaScript)
Collect from the browser:
WebGL renderer + parameters → GPU profile
AudioContext output → audio stack fingerprint
Canvas rendering → 2D render fingerprintThese signals identify the device hardware. They’re harder to spoof than software properties because they depend on physical components and low-level driver behavior. A bot running on a cloud VM cannot produce a WebGL fingerprint that matches a real desktop GPU without intercepting and rewriting the WebGL API calls, and most automation tools don’t do this convincingly for the full parameter set.
Layer 3: Behavioral Fingerprints (JavaScript)
Collect over the session:
Mouse movement patterns → human vs. automated motion
Scroll behavior → natural vs. programmatic
Keystroke timing → cadence analysis
Touch events → pressure, area, timing
Interaction timing → human reaction time vs. script delaysBehavioral signals aren’t traditional “fingerprints” but they provide the strongest bot/human discrimination. A bot can spoof every static property perfectly and still fail behavioral analysis because generating convincing human-like interaction at scale is an unsolved problem. The timing distributions, the micro-corrections in mouse trajectories, the variable scroll velocities: these are hard to fake.
Combining Signals
No single fingerprinting technique is sufficient in 2026. The value is in the combination:
Confidence = f(
network_fingerprint_consistency,
hardware_fingerprint_entropy,
behavioral_signal_humanness,
cross_signal_coherence
)Cross-signal coherence is the key concept. A client claiming to be Chrome 126 on Windows 11 should have:
- A JA4 hash matching Chrome 126’s TLS stack
- A WebGL renderer matching a real Windows GPU (not SwiftShader)
- An AudioContext output consistent with Windows audio drivers
- HTTP/2 settings matching Chrome’s defaults
- Mouse movements with human-like jitter and velocity curves
If any of these signals contradicts the others, something is being spoofed. A real browser is internally consistent. A spoofed browser almost never is, because each spoofing mechanism operates independently and rarely accounts for cross-signal dependencies.
The Privacy Tension
This article has been written from a security perspective. That’s intentional. Fingerprinting for bot detection and fingerprinting for cross-site tracking are the same technology applied to different ends.
The privacy concerns are real. Browser fingerprinting has been used to track users across sites without consent, circumventing cookie controls and privacy preferences. The browser vendor restrictions described above are responses to documented abuse.
The security case is also real. Without fingerprinting, bots are nearly undetectable. CAPTCHAs fail. Rate limiting fails. Behavioral analysis alone has a high false-positive rate. The same signals that let an ad network track you across the web are the signals that let a payment processor detect a credential-stuffing attack.
This is a genuine tension, not a false dichotomy. The current trajectory, where browsers restrict JavaScript-accessible signals while network-level fingerprinting becomes the primary detection vector, is a reasonable compromise. It makes cross-site user tracking harder (you can’t access TLS fingerprints from JavaScript) while preserving the ability of server operators to identify suspicious clients hitting their own infrastructure.
Where this lands in three years is anyone’s guess. But for now, the fingerprinting toolkit is smaller than it was, the signals that remain are more durable than the ones that were lost, and the arms race between detection and evasion shows no signs of slowing down.
Related Reading:
Share this post
Like this post? Share it with your friends!
Want to see WebDecoy in action?
Get a personalized demo from our team.