JA4 Fingerprinting for AI Scraper Detection
Build a JA4 collection and detection pipeline for AI scrapers. Covers ClientHello signals, observed corpora, client mismatches, evasion, and layered rules.
securityCompare canvas, WebGL, audio, and JA4 fingerprinting in 2026: browser privacy protections, bot detection limits, and a practical evaluation checklist.
Browser fingerprinting still works in 2026, but the most useful techniques have shifted. Canvas, WebGL, and AudioContext remain partial signals; reduced browser APIs provide less entropy; and network-level fingerprints such as JA4 are generally more durable because page JavaScript cannot directly rewrite the TLS handshake.
It is not a durable cross-browser identity by itself, and no single fingerprint should be treated as proof that a visitor is human or automated. The strongest defensive use combines fingerprint consistency with behavior, request context, and high-confidence evidence such as decoy interactions.
The useful question is which observations remain available, stable, and relevant in the browser modes your visitors actually use. A privacy setting can change a result without the visitor being a bot. A bot can run the same browser engine as a human.
This guide separates documented API behavior from detection assumptions. It is a technical reference, not a benchmark claiming measured accuracy across every browser. For implementation, start with the evaluation checklist below.
The table summarizes what each technique can tell you and the main reason it can mislead. It does not assign universal entropy or accuracy scores; those require measurements from a defined population.
| Technique | Useful observation | Main limitation |
|---|---|---|
| User-Agent and platform | Claimed browser family, major version, and platform | Reduced detail; claims can be changed |
| navigator.plugins | Standard PDF-viewer compatibility information | Not a list of uniquely installed plugins |
| Canvas | Rendering output for a specific scene | Privacy protections can modify readback |
| WebGL | Available graphics capabilities and rendering output | Extensions can be hidden; software rendering is not proof of automation |
| OfflineAudioContext | Browser audio-processing output | Does not require a physical audio device |
| Fonts and screen properties | Available layout and display characteristics | Shared configurations and restricted APIs reduce distinctiveness |
| Client Hints | Browser-supplied client information where supported | Availability and permitted detail vary |
| TLS fingerprint (JA4) | Characteristics of the TLS client seen at the observation point | Many clients share values; handshakes can be imitated |
| HTTP/2 settings | Connection-level client configuration | Shared stacks and intermediaries complicate attribution |
| TCP/IP characteristics | Network-stack behavior at the observer | Network path and proxies affect interpretation |
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.
Chromium reduced detailed version and platform information in stages. The browser major version remains useful context; the reduced fields are not a device identifier. The Chromium rollout and format reference documents the retained and replaced fields, including the alternative Client Hints interfaces.
A claimed browser family can still help check consistency against other observations. It should not become a block decision on its own.
Verdict: Low-detail context, with no assurance that a client is telling the truth.
These APIs once exposed information about installed plugins. Modern navigator.plugins is specified to return five standard compatibility entries when inline PDF viewing is supported, or an empty list otherwise. It is not always empty, and an empty list alone is not a headless-browser signature. See the MDN API reference.
Verdict: Keep only if the observed compatibility state contributes to a validated consistency check. Do not treat plugin count as a unique identity.
navigator.platform exposes a broad platform label. Values such as Win32 do not reliably describe the actual processor architecture. Where supported, Client Hints offer a separate interface for requesting additional client information; support and permitted values must be checked.
Verdict: A compatibility hint, not a hardware inventory or an authenticated platform claim.
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.
Canvas readback needs to be evaluated by browser mode:
privacy.resistFingerprinting. See Firefox’s protection documentation.The following example demonstrates readback, not uniqueness. Handle unavailable contexts and collection failures separately from successful observations.
// Basic canvas fingerprint
function getCanvasFingerprint() {
const canvas = document.createElement('canvas');
canvas.width = 256;
canvas.height = 256;
const ctx = canvas.getContext('2d');
if (!ctx) return null;
// 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: A partial rendering signal. This article has no measured basis for an “80% of browsers” coverage claim; measure availability and stability on your traffic.
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.).
Browser restrictions and shared font configurations limit how identifying this can be. Loading a web font does not, by itself, prevent a script from probing system fonts. Access also depends on the collection method and browser protections.
Verdict: A supporting layout observation. Test the permitted font surface on each browser and avoid inferring a person from an apparent font match.
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.
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.
Availability matters: the WEBGL_debug_renderer_info extension can be unavailable under privacy settings. Firefox disables it when privacy.resistFingerprinting is enabled. Code should handle a missing extension normally. See MDN’s extension reference.
A renderer string such as SwiftShader describes software rendering. It does not establish that a visitor is a bot: graphics configuration and fallback behavior need context. Equally, a bot can run real Chromium with hardware acceleration. Chrome documents that its modern headless and headful modes share an implementation.
Verdict: Useful graphics context and consistency evidence. Neither a renderer name nor missing WebGL is a sufficient automation verdict.
An OfflineAudioContext renders an audio graph into a buffer without playing it through a physical output device. Differences in implementation and processing can contribute a signal, but the result should not be described as a direct sound-card or speaker fingerprint. See the API reference.
This example computes a summary of a rendered buffer. Repeated values do not establish a unique device.
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);
});
});
}A headless environment can implement offline audio processing without physical audio hardware. An unavailable context or an unexpected value is therefore not proof of automation. Privacy settings can also alter exposed outputs.
Verdict: Another processing observation to evaluate alongside other signals. Measure whether it adds useful information before collecting it in production.
JA4 summarizes features of a TLS ClientHello. Collection happens at a server, proxy, or edge that can observe the handshake, rather than through page JavaScript. The FoxIO JA4 repository documents the format and implementations.
Page JavaScript cannot directly set the handshake parameters of its browser. The client operator can, however, use a different network stack or an implementation that imitates another client. Automated and human-driven copies of the same browser can share a JA4 value. A TLS-terminating intermediary can also change which client your origin observes.
Use JA4 to group observations and investigate inconsistencies with claimed clients. It does not identify an individual, prove use of Playwright, or establish which model powers an agent.
To inspect a value, use the JA4 lookup and decoder. For collection and detection design, read the JA4 implementation guide.
Verdict: Useful network context with shared values and evasion limits. Evaluate it with request behavior rather than treating it as a universal block key.
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.
HTTP/2 settings add connection context, but are correlated with the client stack and can be affected by intermediaries. Measure their incremental value alongside TLS observations; two matching fingerprints are not two independent proofs of identity.
Verdict: Additional connection context where available. Validate how proxies and protocol changes affect the observations.
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 protections vary by product, version, platform, and browsing mode. Keep these distinctions in your test matrix:
The browser-vendor references above describe mechanisms. Your own measurements should establish their effect on the detection decisions you actually make.
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: Hosted automation can run real browser engines. Do not assume the provider uses an obsolete Chromium release or has a unique TLS signature without a dated, reproducible measurement.
Anti-detect browsers: Modified profiles can alter exposed properties. A detector must evaluate the resulting evidence, rather than treating the product name or a vendor’s spoofing claim as a measured detection result.
If you’re building fingerprinting into a product today, here’s the architecture that accounts for the current reality.
Collect at the proxy or load balancer level:
TLS ClientHello → JA4 hash
HTTP/2 SETTINGS → settings fingerprint
TCP/IP characteristics → OS fingerprintThese observations are outside page JavaScript, but remain influenced by the client stack and observation point. Investigate a mismatch with the claimed browser against a current baseline and the known proxy path.
Collect from the browser:
WebGL renderer + parameters → GPU profile
AudioContext output → audio stack fingerprint
Canvas rendering → 2D render fingerprintThese observations describe the rendering and processing environment, with privacy and availability limits. Keep missing values distinct from observed values. A graphics driver change or browser update can legitimately change a fingerprint.
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 observations add context about the interaction rather than just its environment. Measure them across legitimate navigation styles, mobile devices, assistive technology, and scripted tests. A quiet mouse or an unusual cadence should not by itself trigger enforcement.
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 means asking whether the observations make sense together. An apparent mismatch may come from automation, a proxy, a browser update, or privacy controls. Record the evidence and test those alternatives before choosing a response.
For a practical deployment path, Bot Scanner covers browser-side collection, the Edge Sensor covers requests outside the browser script, and decoy links add interaction evidence.
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.
Security decisions still need evidence when fingerprint data is limited. Use authentication, authorization, abuse controls, request history, and detection signals appropriate to the action being protected. A fingerprint can support that system; it cannot replace it.
Limit collection and retention to the security question you are answering. Account for privacy-preserving browsers when evaluating false positives. The practical goal is to reduce abuse while allowing legitimate visitors to complete their tasks.
Related Reading:
It can support risk assessment, but reliability depends on the browser, privacy settings, and traffic population. Canvas, WebGL, audio, and network fingerprints describe parts of a client environment. None alone proves that a visitor is human or automated.
User-Agent reduction removes some detailed browser and operating-system information. It does not eliminate every fingerprinting surface. Rendering APIs, Client Hints, and network observations have different capabilities and limits, so they need separate evaluation.
It can expose inconsistencies or known automation markers, but real-browser automation can share legitimate browser fingerprints. Missing APIs or randomized values can also come from privacy protections. Combine signals with behavior and request context before taking action.
There is no universal winner. JA4 provides network context that page JavaScript cannot directly change, while canvas and WebGL provide rendering observations. Clients can share or imitate fingerprints. Measure each signal's coverage, stability, and false-positive rate on your own traffic.
Clearing cookies does not necessarily change rendering or network characteristics. Switching browsers, updating software, or enabling privacy protections can change them. A fingerprint is not a guaranteed persistent identity across browsers or devices.
Build a JA4 collection and detection pipeline for AI scrapers. Covers ClientHello signals, observed corpora, client mismatches, evasion, and layered rules.
securityA merchant's guide to authorized AI shopping agents, checkout delegation, inventory scraping, carding, coupon abuse, and fake agent identities.
securityHow Web Bot Auth, ARD, OAuth, and workload identity fit together to authenticate AI agents, preserve user delegation, and create auditable access.
securityLike this post? Share it with your friends!
Get a personalized demo from our team.