Every HTTPS connection begins with a handshake, and the very first message a client sends, the ClientHello, is unencrypted. It advertises which TLS version the client supports, which cipher suites it prefers, which extensions it wants, and in what order it lists all of them. That ordering and selection is a byproduct of the TLS library baked into the client, not something the user configures. A stock Chrome build produces one shape; Python’s requests, Go’s net/http, and a headless scraper each produce a different one. TLS fingerprinting turns that shape into a stable identifier you can read before a single byte of application data arrives.
JA4 is the current generation of TLS client fingerprinting. It replaces the older JA3 format with something more structured, harder to spoof by accident, and readable by a human instead of an opaque hash. If you run any kind of bot detection, understanding the ja4 fingerprint gives you a signal that lives entirely on the server, costs nothing to collect, and catches automation that has not bothered to disguise its network stack.
What goes into a JA4 fingerprint
JA4 is built from fields in the ClientHello. Rather than concatenating everything into one hash the way JA3 did, it breaks the fingerprint into readable segments and hashes only the parts that benefit from compression.
The client portion, JA4, is composed of three parts joined by underscores:
- A prefix describing the transport and protocol: whether the connection is TLS over TCP (
t) or QUIC (q), the negotiated TLS version, whether a SNI is present, the count of cipher suites, the count of extensions, and the first ALPN value. - A truncated hash of the sorted list of cipher suites.
- A truncated hash of the sorted list of extensions plus the signature algorithms.
The critical design choice is sorting. JA3 hashed ciphers and extensions in the exact order the client sent them, which meant a browser that shuffled its extension order per connection, as Chrome now does with GREASE and extension permutation, produced a new JA3 on every handshake. JA4 sorts the lists before hashing, so the fingerprint stays stable across those deliberate shuffles while still capturing which values are present.
JA4 = t13d1516h2_8daaf6152771_b186095e22b6
│ │ └─ hash of extensions + sig algorithms
│ └─ hash of sorted cipher suites
└─ t=TCP, 13=TLS 1.3, d=SNI present, 15 ciphers, 16 extensions, h2=ALPN
Because the first segment is left in the clear, you can read a lot without decoding the hashes at all. t13d1516h2 tells you this is a TLS 1.3 connection over TCP negotiating HTTP/2 with a server name present, which already rules out large classes of naive clients.
Why JA4 beats JA3
JA3 was a genuine advance when it appeared, but it aged poorly. Its weaknesses are exactly what JA4 was designed to fix.
| Property | JA3 | JA4 |
|---|---|---|
| Field ordering | Preserved raw order | Sorts ciphers and extensions |
| Stability under GREASE | Breaks | Stable |
| Output format | Single MD5 hash | Structured, part human-readable |
| Transport awareness | TCP only in practice | Distinguishes TCP and QUIC |
| ALPN visibility | Buried | Exposed in the prefix |
The practical result is fewer distinct fingerprints for the same browser and less noise. A single Chrome version might have produced dozens of JA3 hashes across its GREASE variations; under JA4 it collapses to one or a small handful. That makes allowlisting and anomaly detection tractable in a way JA3 never was at scale.
Reading JA4 in practice
The value of a ja4 fingerprint comes from comparison, not from the string in isolation. You build or borrow a database that maps known-good fingerprints to the clients that produce them, then flag mismatches.
Common patterns worth alerting on:
- A JA4 that maps to a scripting library (
python,go-http,okhttp) arriving at a login or checkout endpoint that a human would reach through a browser. - A JA4 consistent with an old TLS stack while the
User-Agentheader claims a brand-new browser version. The network layer and the header layer disagree, which is a classic sign of a spoofed header on a scraper. - A single JA4 fanned out across thousands of IP addresses, which points at one tool behind a residential proxy pool.
The mismatch case is the most useful. Headers are trivial to forge; the TLS stack is not, unless the attacker has specifically invested in matching it. When the advertised User-Agent and the observed JA4 tell different stories, you are almost always looking at automation.
Where JA4 stops, and what fills the gap
JA4 is a strong signal with a real ceiling. Libraries like utls, curl-impersonate, and BoringSSL forks let a determined operator craft a ClientHello byte-for-byte identical to Chrome, producing a JA4 that matches a genuine browser. At that point the network layer alone tells you nothing, because it was engineered to lie.
This is why JA4 belongs in a layered stack rather than as a gate on its own:
- Pair it with the full JA4+ suite. JA4H fingerprints the HTTP layer, JA4T the raw TCP characteristics, and JA4S the server response, so an attacker has to fake several independent layers consistently.
- Cross-check it against client-side device fingerprinting. A ClientHello that says Chrome but a JavaScript environment that reveals a headless runtime is a contradiction no spoofing tool resolves for free.
- Feed it into a confidence score alongside behavioral and reputation signals rather than treating one mismatch as a verdict.
An attacker who perfectly clones the TLS stack usually still trips a headless Chrome check or a behavioral anomaly, because matching every layer at once is far harder than matching any single one.
Frequently asked questions
What is the difference between JA4 and JA3?
JA3 was an unordered MD5 hash of a raw list of ClientHello values, which made it brittle and easy to collide. JA4 is a structured, human-readable fingerprint that sorts ciphers and extensions and separates the hello into readable parts, so it resists trivial reordering and is easier to reason about.
Can a client change its JA4 fingerprint?
Yes. Tools like utls and curl-impersonate can craft a ClientHello that mimics a real browser, producing a matching JA4. That is why JA4 is a strong prior, not a verdict on its own, and works best combined with client-side device signals.
Does JA4 require decrypting TLS traffic?
No. JA4 is computed from the ClientHello, which is sent in the clear before the encrypted session begins, so you can fingerprint at the edge without terminating or decrypting the payload.
TLS fingerprinting is one of the cheapest high-signal checks available to a fraud team, because it reads a client’s true network stack before any application logic runs. Treated as a prior and combined with device and behavioral evidence, the ja4 fingerprint quietly filters out the large share of automation that never bothered to disguise itself, and raises the cost for the automation that did. See how it fits the wider picture in our glossary and try it against live traffic in the playground.
Run it yourself
Prynt is open-source, self-hostable device intelligence — visitor IDs, bot & fraud Smart Signals, and behavioral biometrics you own end to end.