Anti-Bot

What Is JA4 Fingerprinting?

What Is JA4 Fingerprinting? — conceptual illustration
On this page

JA4 is a TLS client fingerprint that replaced JA3 after Chrome began randomising the order of its TLS extensions. JA3 hashed the extension list in the exact order sent, so when Chrome 110 shuffled that order on every connection, a single browser produced billions of different JA3 hashes and the fingerprint became useless for blocklisting. JA4 fixes this by sorting the ciphers and extensions before hashing, producing one stable value per client. It is the lead member of the JA4+ suite and, by 2026, the de-facto standard at Cloudflare, Akamai, and AWS.

Quick facts

ReplacesJA3 — broken by Chrome’s randomised extension order since Chrome 110
Key fixSorts ciphers + extensions before hashing → one stable hash per client
FormatHuman-readable prefix + two truncated SHA-256 hashes (e.g. t13d1516h2_8daaf6152771_b186095e22b6)
JA4+ suiteJA4 (TLS), JA4S (server), JA4H (HTTP), JA4L (latency), JA4X (cert), JA4SSH
Adopted byCloudflare, Akamai, AWS, VirusTotal (industry standard in 2026)

Why JA3 broke and JA4 was needed

JA3 built its hash from the TLS Client Hello by concatenating the version, cipher suites, extensions, elliptic curves, and curve formats in the order the client sent them, then MD5-hashing the result. That order was stable per client for years, which made JA3 an excellent blocklist key.

In Chrome 110 (early 2023) Google shipped TLS extension-order randomisation as an anti-ossification measure: Chrome shuffles the order of its extensions on every connection. Overnight, a single Chrome install started producing a different JA3 hash on nearly every request — effectively billions of values. Blocklisting a JA3 became pointless, and ironically a scraper that sent a fixed extension order now stood out against real Chrome’s shuffling.

JA4 solves this by sorting the cipher and extension lists before hashing. Order no longer matters, so Chrome’s randomisation collapses back to a single stable JA4. The trade-off — throwing away order information — is recovered elsewhere in the JA4+ suite.

How a JA4 fingerprint is built

Unlike JA3’s single opaque MD5, JA4 is deliberately human-readable in three parts, joined by underscores:

  1. Prefix (a/b/c). Protocol (t for TCP, q for QUIC), TLS version (13 = 1.3), SNI present (d = domain, i = IP), two-digit cipher count, two-digit extension count, and the first ALPN value (h2 for HTTP/2). Example: t13d1516h2.
  2. Hash B. The first 12 hex chars of a SHA-256 over the sorted cipher-suite list.
  3. Hash C. The first 12 hex chars of a SHA-256 over the sorted extension list plus the signature algorithms.

The result looks like t13d1516h2_8daaf6152771_b186095e22b6. An analyst can read the prefix at a glance — TLS 1.3, 16 ciphers, 15 extensions, HTTP/2 — without decoding anything, and the two hashes serve as the precise match key.

The JA4+ suite — fingerprinting the whole connection

JA4 alone fingerprints the TLS Client Hello. The power of the approach is the + suite that fingerprints other layers and is cross-checked for coherence:

  • JA4S — the server’s response (cipher chosen, extensions).
  • JA4H — the HTTP layer: method, version, header order, cookie and referer presence, accept-language. This is what catches a client that nails the TLS JA4 but sends Python-shaped headers.
  • JA4L — latency / round-trip timing, used to estimate physical distance and spot proxy hops.
  • JA4X — X.509 certificate fingerprint.
  • JA4SSH — SSH session fingerprint.

A scraper is scored on all relevant members at once. Matching Chrome’s JA4 while failing JA4H is the single most common tell — it happens whenever a library wraps a Chrome-impersonating TLS stack around its own HTTP implementation.

What it means for scrapers

The Python ssl / requests stack produces a JA4 that no browser ever sends — an instant block at any JA4-aware vendor. The fix is a TLS library that impersonates a real browser’s Client Hello byte-for-byte: curl_cffi (libcurl + BoringSSL with Chrome presets), tls-client, or rustls-based impersonators. These reproduce the cipher list, extensions, supported groups, and ALPN so the sorted JA4 matches a current Chrome.

Two pitfalls remain. First, cross-layer coherence — the JA4H must also match, which most HTTP clients get wrong (header order, casing, pseudo-header order). Second, version drift — a JA4 frozen to Chrome 120 while live Chrome is on 133 becomes its own anomaly, so impersonation presets need updating. The most reliable answer is to drive a real browser engine, or a build that maintains current presets, rather than hand-assembling a Client Hello. This is the same coherence problem clustering exploits, pushed down to the network layer.

Code example

text
# A JA4 fingerprint, decoded.

t13d1516h2_8daaf6152771_b186095e22b6
│││││││││ │            └─ SHA-256 of sorted extensions + sig-algs (12 hex)
│││││││││ └──────────── SHA-256 of sorted cipher suites      (12 hex)
│││││││└─ ALPN  : h2  (HTTP/2)
│││││└─ ext count : 16
│││└─ cipher count : 15
││└─ SNI : d  (server name = domain)
│└─ TLS version : 13  (TLS 1.3)
└─ transport : t  (TCP;  q = QUIC)

# JA3 hashed the extensions in send-order, so Chrome's randomisation
# produced a new MD5 every connection. JA4 sorts first -> one stable value.
# Python's default ssl stack yields a JA4 no browser sends == instant block.

Related terms

Concept map

How JA4 Fingerprinting connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Anti-Bot
Building map…

Frequently asked questions

Is JA4 better than JA3 at catching bots?

For modern Chrome traffic, yes — JA3 is effectively dead because Chrome randomises extension order, so one browser yields billions of JA3 hashes. JA4 sorts before hashing and stays stable, restoring it as a usable blocklist key. JA4 is also more expressive: the readable prefix encodes TLS version, cipher and extension counts, and ALPN, and the JA4+ suite extends fingerprinting to the HTTP, QUIC, latency, and certificate layers.

Can curl_cffi or tls-client beat JA4?

They can match the TLS-layer JA4 by impersonating a real Chrome Client Hello byte-for-byte. What they frequently fail is the HTTP-layer JA4H — header order, casing, and pseudo-header order rarely match the browser they claim to be. JA4 and JA4H are scored together, so matching one while failing the other is itself the signal.

Does randomising my own TLS fingerprint help?

No. Real Chrome randomises extension order but JA4 normalises that away, so after sorting every real Chrome lands on the same JA4. A scraper that randomises ciphers or supported groups produces JA4 values no real client sends, which stands out more, not less. The goal is to match one current browser exactly, not to vary.

Last updated: 2026-05-28