Anti-Bot

What Is JA4 Fingerprinting?

By the Scrappey Research Team

What Is JA4 Fingerprinting? — conceptual illustration
On this page

JA4 is a way to identify a browser by the fingerprint of its TLS handshake — TLS being the encryption layer behind https. It replaced the older JA3 method after Chrome started randomising the order of its TLS extensions. When a browser opens a secure connection it sends a list of supported settings (ciphers, extensions, and so on). JA3 turned that list into a single hash using the exact order the browser sent it. The problem: in Chrome 110, Chrome began shuffling that order on every connection, so one browser produced billions of different JA3 hashes — making the fingerprint useless for blocking. JA4 fixes this by sorting the ciphers and extensions before hashing, so every connection from the same client produces one stable value. 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

Every TLS connection starts with a Client Hello — the opening message where the browser lists what it supports. JA3 built its hash by taking five things from that message (the TLS version, cipher suites, extensions, elliptic curves, and curve formats) and stringing them together in the exact order the client sent them, then running MD5 over the result (MD5 is just a function that turns any input into a fixed-length code). For years a given browser always sent these in the same order, so JA3 made an excellent key for blocklisting.

That broke in Chrome 110 (early 2023), when Google shipped TLS extension-order randomisation. The goal was anti-ossification — stopping middleware on the internet from hard-coding assumptions about Chrome's traffic — so Chrome now shuffles the order of its extensions on every connection. Overnight, a single Chrome install began producing a different JA3 hash on nearly every request, effectively billions of values. Blocklisting a JA3 became pointless. Worse, the tables turned: a scraper that always sent a fixed extension order now stuck out, because real Chrome was constantly shuffling.

JA4 solves this by sorting the cipher and extension lists before hashing. Once everything is sorted, order no longer matters, so Chrome's randomisation collapses back to a single stable JA4. The cost of throwing away order information is paid back elsewhere in the JA4+ suite.

How a JA4 fingerprint is built

JA3 was one opaque MD5 string you could not read. JA4 is deliberately human-readable in three parts, joined by underscores:

  1. Prefix (a/b/c). A readable summary: protocol (t for TCP, q for QUIC), TLS version (13 = 1.3), whether a hostname was sent (d = domain, i = IP), a two-digit count of ciphers, a two-digit count of extensions, and the first ALPN value (the protocol the client wants to speak — h2 means HTTP/2). Example: t13d1516h2.
  2. Hash B. The first 12 hex characters of a SHA-256 hash over the sorted cipher-suite list.
  3. Hash C. The first 12 hex characters of a SHA-256 hash over the sorted extension list plus the signature algorithms.

The full value 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, while the two hashes act as the precise match key.

The JA4+ suite — fingerprinting the whole connection

JA4 on its own only fingerprints the TLS Client Hello. The real strength comes from the + suite, a set of related fingerprints for other layers of the connection that are cross-checked against each other for consistency:

  • JA4S — the server's response (which cipher it picked, which extensions).
  • JA4H — the HTTP layer: request method, version, the order of headers, whether a cookie and referer are present, accept-language. This is what catches a client that gets the TLS JA4 right but sends Python-shaped HTTP headers.
  • JA4L — latency, meaning round-trip timing, used to estimate physical distance and spot proxy hops in between.
  • JA4X — a fingerprint of the X.509 certificate (the document that proves a server's identity).
  • JA4SSH — a fingerprint of an SSH session.

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

What it means for scrapers

The default Python ssl / requests stack produces a JA4 that no browser ever sends, which means an instant block at any JA4-aware vendor. The fix is a TLS library that copies 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 exact cipher list, extensions, supported groups, and ALPN so that, once sorted, the JA4 matches a current Chrome.

Two traps remain. First, cross-layer coherence: the JA4H has to match too, and most HTTP clients get this wrong (header order, capitalisation, the order of pseudo-headers). Second, version drift: a JA4 frozen to Chrome 120 while live Chrome is on 133 becomes an anomaly of its own, so impersonation presets need regular updating. The most reliable approach is to drive a real browser engine, or use a tool that keeps its presets current, rather than hand-building a Client Hello. It is the same coherence problem clustering exploits, just 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

What Is TLS Fingerprinting (JA3/JA4)?
TLS fingerprinting is a way to recognize what software made a connection just by looking at how it sets up encryption — before the server re…
What Is HTTP/2 Fingerprinting?
HTTP/2 fingerprinting identifies an HTTP client from its SETTINGS frame and frame-level behaviour, independent of the TLS layer. Think of it…
What Is Fingerprint Clustering?
Fingerprint clustering is the practice of grouping fingerprints from millions of real visitors by similarity, then rejecting any new visitor…
What Is Fingerprint Lie Detection?
Fingerprint lie detection is the practice of verifying that the signals a browser reports are internally consistent and untampered, rather t…
What Is curl_cffi?
curl_cffi is a Python HTTP client whose TLS fingerprint looks exactly like real Chrome, Firefox, or Safari. TLS is the encryption layer behi…
What Is Anti-Bot Detection?
Anti-bot detection is the set of techniques websites use to tell automated traffic apart from real human visitors — and then block, challeng…
Anti-Bot Vendor Detection Cheatsheet
A useful first step when working with any protected site you are authorized to access is identifying which anti-bot vendor sits in front of …
What Is Cloudflare Bot Management?
Cloudflare Bot Management is the enterprise-tier ML scoring system Cloudflare runs on every request to a protected zone. In plain terms: it …
What Is JA3 Fingerprinting?
JA3 is a method for fingerprinting a TLS client by hashing the fields of its Client Hello. TLS is the encryption layer behind https, and the…

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 a single browser yields billions of JA3 hashes. JA4 sorts the list before hashing, so it stays stable — which makes it usable again as a blocklist key. JA4 is also more informative: its readable prefix spells out the TLS version, the cipher and extension counts, and the ALPN protocol, 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, because they copy a real Chrome Client Hello byte-for-byte. What they frequently fail is the HTTP-layer JA4H — header order, capitalisation, 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 that gives you away.

Does randomising my own TLS fingerprint help?

No. Real Chrome does randomise its extension order, but JA4 sorts that away — so after sorting, every real Chrome lands on the exact same JA4. A scraper that randomises its ciphers or supported groups just produces JA4 values that no real client ever sends, which makes it stand out more, not less. The goal is to match one current browser exactly, not to vary.

Last updated: 2026-05-31