The blind spot above the HTTP layer
Most bot-detection signals — User-Agent strings, cookies, headers — live at the HTTP layer. That's also the layer a script controls completely; it can set any header to any value. But before any of that HTTP data exists, an HTTPS connection has to complete a TLS handshake, and that handshake happens at a lower layer that a typical HTTP client library doesn't give the script fine-grained control over. That gap is where TLS fingerprinting — commonly known by the technique's original name, JA3, and its successor, JA4 — does its work.
How JA3 actually works
JA3, published by Salesforce's security team in 2017, fingerprints the very first message of a TLS handshake: the client's ClientHello. That message includes several fields the client chooses, in an order the client chooses:
- The TLS version it offers
- The list of cipher suites it supports, in order
- The TLS extensions it sends, in order
- The elliptic curves it supports
- The elliptic curve point formats it supports
JA3 concatenates those five fields into one string and hashes it with MD5, producing a short fixed-length fingerprint — Chrome's, for instance, is stable enough that it's widely published and re-derivable by anyone running the same version.
ClientHello fields → "771,4866-4867-4865-...,0-23-65281-10-11-...,29-23-24,0"
→ MD5 hash
→ JA3 = "cd08e31494f9531f560d64c695473da9"
Why the handshake reveals automation
Every TLS implementation — the one built into Chrome, the one in Python's requests (via OpenSSL), the one in Go's standard library, the one in curl — negotiates this handshake slightly differently, because each ships its own list and ordering of supported ciphers, extensions, and curves. A browser's TLS stack and a scripting language's default HTTP client stack essentially never match.
A request claiming User-Agent: Chrome while its TLS fingerprint matches Python's default client stack is a strong, specific signal — the two facts about the same connection simply don't agree, and unlike a header, the TLS fingerprint isn't something the calling script's HTTP library typically exposes an easy setting for.
The Chrome ClientHello randomization problem
Browser vendors have their own reasons to dislike fingerprinting stability, separate from bots — a fingerprintable browser is also trackable across sites. Starting around Chrome 110 in 2023, Google began randomizing the order of TLS extensions in Chrome's ClientHello (a technique related to "GREASE," designed to stop the TLS ecosystem from ossifying around any one fixed set of values). That randomization means two requests from the exact same Chrome install can legitimately produce different JA3 hashes, which broke JA3's original assumption that one browser version means one stable fingerprint.
The silver lining: the randomization is itself a signature. Real Chrome installs randomize in a specific, recognizable way; a scripted client that hasn't specifically replicated Chrome's randomization behavior still stands out, and a static, never-changing fingerprint from something claiming to be Chrome is now, if anything, a stronger tell than before.
JA4: the successor
JA4, published by FoxIO in 2023, was designed partly in response to exactly this problem. Rather than hashing the raw, order-sensitive field list, JA4 sorts ciphers and extensions before hashing — which makes the fingerprint resilient to extension-order randomization instead of broken by it. A few other differences from JA3:
- Uses SHA-256 instead of MD5.
- Produces a structured, partially human-readable fingerprint (protocol, TLS version, cipher/extension counts, then hash segments) instead of one opaque 32-character string.
- Extends to QUIC/HTTP-3, which JA3 predates.
JA4 has seen adoption across several major CDN and WAF providers since publication, generally as one input alongside other fingerprinting layers rather than a replacement for all of them.
One layer further: HTTP/2 fingerprinting
A client sophisticated enough to spoof its TLS fingerprint (tools like curl-impersonate and utls-based libraries exist specifically for this) can still be caught one layer up, at HTTP/2. The SETTINGS frame values, window-update behavior, and pseudo-header ordering a client sends when establishing an HTTP/2 connection form their own fingerprint — often called an Akamai-style HTTP/2 fingerprint — and it's a separate implementation detail from the TLS stack, so a client has to get both layers right to pass unnoticed.
What it can't do alone
TLS fingerprinting is not a silver bullet, and treating it as one creates its own false confidence:
- Popular fingerprints aren't unique. Millions of real users share the exact same JA4 as any given Chrome release — the fingerprint identifies the client software, not the individual visitor.
- Carrier-grade NAT complicates IP-based correlation. Many real users share a single public IP behind carrier NAT, so pairing "this IP" with "this fingerprint" for identity purposes is noisier on mobile networks.
- Impersonation tooling exists and works. Libraries built specifically to replicate a target browser's TLS fingerprint narrow the gap between a script and a real browser at this layer.
- Plenty of legitimate non-browser clients exist. Mobile apps, webhooks, monitoring services, and search-engine crawlers all have their own distinct, entirely legitimate TLS stacks that will never match a browser's fingerprint and shouldn't be penalized for it.
This is why Botscope treats TLS fingerprinting as one signal feeding a combined score — alongside ASN reputation, device fingerprinting, and behavioral analysis — rather than a standalone verdict. See our pieces on residential proxy detection and proactive protection for how the other layers fit together.
FAQ
Can a bot just fake a browser's TLS fingerprint?
To a degree, yes — tools exist specifically to replicate a target browser's handshake. It raises the cost and sophistication required, though, which is the realistic goal of any single layer: not making evasion impossible, but making it expensive enough that it stops being worthwhile for most automated traffic.
Does TLS fingerprinting slow down my site?
No — the fingerprint is computed from data already present in the handshake your server completes anyway, so there's no extra round trip. The lookup against known fingerprints adds negligible latency when cached properly.
Is JA4 a drop-in replacement for JA3?
Functionally, JA4 supersedes JA3 for most new deployments, since it's resilient to the same ClientHello randomization that undermined JA3's stability. Many detection systems still track both, since a large amount of existing threat-intelligence data was built around JA3 hashes.