DEV Community

Cover image for JA4 Fingerprint: What It Reads and Why JA3 Stopped Working
ParallaxGrain
ParallaxGrain

Posted on

JA4 Fingerprint: What It Reads and Why JA3 Stopped Working

Two separate connections from the same Chrome open with a ClientHello, the opening message of each HTTPS handshake, and the extensions inside it come in a different order each time. Hash that list in the order it arrives, as JA3 does, and one browser produces a new fingerprint on nearly every connection. A JA4 fingerprint sorts the list first, so both connections give the same string, one that a rule or a database can match. Cloudflare, AWS WAF and Auth0 all read JA4 now.

A TLS fingerprint names the software that opened a connection. It says nothing about who is using that software or why.

Why the JA3 fingerprint stopped working

JA3 came out of Salesforce in 2017. It takes five fields from the ClientHello (TLS version, cipher suites, extensions, elliptic curves and curve point formats), lists the values in ClientHello order and hashes the string with MD5. The same application sends the same hello and gets the same 32-character hash, so defenders could trade hashes of known malware between organizations. The JA3 repository is archived now, and its author maintains JA4 instead.

JA3 broke on ordering, which TLS leaves free: extensions may appear in any sequence, except that pre_shared_key must come last. Servers that key on Chrome's exact layout would make it hard for Chrome to change TLS later, so since Chrome 110 (2023) it shuffles its extensions on every new connection. The shuffle happens inside BoringSSL, Chromium's TLS library, which draws a fresh permutation for each handshake, so browsers built on Chromium's network stack inherit it. A JA3 rule written for one Chrome handshake now matches only the connections that happen to repeat that order.

GREASE, the reserved placeholder values Chrome mixes into its lists to keep servers tolerant of unknown options, did not cause the breakage: JA3 skips them. Randomizing the cipher list, called cipher stunting, is another known way to dodge JA3.

What a JA4 fingerprint reads

JA4, published by FoxIO in 2023, reads the same ClientHello and prints three parts joined by underscores. Taken apart, a Chrome fingerprint like t13d1516h2_8daaf6152771_02713d6af862 says:

  1. The first part is plain text. It opens with t for TLS over TCP (q is QUIC, d is DTLS), then 13 for the highest TLS version the client offers, here 1.3, then d if a server name was sent (i if not). Next come the counts of cipher suites (15) and extensions (16), and h2, the first and last character of the client's first ALPN protocol. That is the one it prefers: HTTP/2.
  2. The second part is the cipher suites sorted in hex order, hashed with SHA-256 and cut to 12 characters.
  3. The third part is the extensions sorted the same way (server name and ALPN left out, because part one covers them), followed by the signature algorithms in the order sent, hashed and cut the same way.

GREASE values are ignored everywhere. Sorting is what cancels the shuffle: every ordering of the same extensions sorts to the same list. The signature algorithms stay in sent order, so a client that shuffled those would still change part three. For cipher suites the order is a real preference ranking, so sorting trades a little detail for stability.

The readable first part is useful without any database lookup. Browsers advertise h2 there, while many scripting clients offer no ALPN at all and show 00 instead.

JA4 is one member of the JA4+ family: JA4S fingerprints the server's reply, JA4H the HTTP request, JA4T the TCP handshake, JA4L latency, JA4X certificates. The licenses differ. The TLS fingerprint itself is open source under BSD 3-Clause, as JA3 is, while the other family members use the FoxIO License, which allows internal and academic use and not selling it inside a product without a separate license.

JA3 vs JA4 side by side

JA3 JA4
Introduced 2017, Salesforce 2023, FoxIO
Fields read TLS version, ciphers, extensions, elliptic curves, point formats Transport, TLS version, server name present or not, ALPN, ciphers, extensions, signature algorithms
Order As sent Ciphers and extensions sorted, signature algorithms as sent
GREASE Skipped Skipped
Transport Not recorded First character: t, q or d
Output One 32-character MD5 hash Three readable parts
Same Chrome, two TCP connections Two different hashes The same fingerprint

The full background on both formats and on the JA4+ family is in the TLS fingerprinting guide.

How bot detection uses a TLS fingerprint

The handshake comes before the first HTTP request, and its shape comes from the TLS code inside the client, not from anything a script writes into a header. A script's HTTP library brings its own TLS defaults, while Chrome uses BoringSSL. A server therefore holds a claim (the User-Agent says Chrome) and a measurement (the handshake says otherwise), and three checks turn that into a signal:

  • Claim against handshake. A Chrome User-Agent on a handshake that looks like a script library is a mismatch. A JA4 nobody has seen before proves nothing by itself, because it can be just a new browser release.
  • One layer against the next. HTTP/2 has its own fingerprint: which SETTINGS values the client sends and in what order, the WINDOW_UPDATE increment, any PRIORITY frames and the sequence of the request pseudo-headers. TCP has JA4T. Different parts of the client produce each layer, so a script that copies Chrome's ClientHello but keeps its library's HTTP/2 defaults disagrees with itself.
  • The fingerprint's own record. Cloudflare's Bot Management can report, for each JA4, what happened over the last hour of its traffic: the share of requests with browser user agents, how many distinct IPs use it, how much it requests. A fingerprint seen almost only on non-browser traffic reads differently from one that a great many browsers share.

Matching all of this is possible. Libraries such as curl_cffi impersonate a real browser's ClientHello and HTTP/2 settings. Another route is to drive an actual browser, whose handshake is genuine by construction. Then the TLS layer has nothing to flag and the decision falls to other signals, which fake browser detection covers for headless and anti-detect browsers.

What a TLS fingerprint can't prove

  • A proxy that terminates TLS replaces the fingerprint. Antivirus that inspects HTTPS and corporate gateways decrypt the traffic and start a second connection of their own, so the server sees the gateway's handshake, which can disagree with an honest browser's User-Agent.
  • Encrypted Client Hello (ECH) hides the real hello from the path. It wraps the true ClientHello inside an outer one. A network observer can fingerprint only the outer hello, while the server that decrypts it still reads the inner one.
  • Over HTTP/3 a browser has a second JA4, one starting with q instead of t, because TLS runs inside QUIC. The first QUIC packet is protected with keys derived from a connection ID sent in the clear, so any observer along the route can read the ClientHello in it.

See your own TLS fingerprint

  1. Start the Privacy & Trust Index scan and wait until every card has a result.
  2. Find the TLS / JA4 Fingerprint card. It reads your TLS and HTTP/2 handshake and compares both with the browser your User-Agent reports. The answer is CONSISTENT, ENGINE MISMATCH, SPOOFED, AUTOMATION TOOL or UNVERIFIED. ENGINE MISMATCH can also come from a corporate proxy or a brand-new browser build, and UNVERIFIED means your fingerprint is not in the reference set yet, so nothing is concluded.
  3. Scroll to the Connection Fingerprint panel for the raw values: JA4, the older JA3, ECH status, the TCP row with JA4T and the HTTP/2 fingerprint.

Top comments (0)