Learning centre

Bots & traffic

7 min readUpdated

What is TLS fingerprinting (JA3 and JA4)?

Quick answer

TLS fingerprinting identifies what software is making a connection by looking at how it negotiates encryption, rather than at what it claims to be. Every client sends a Client Hello listing the cipher suites, extensions and curves it supports, in a particular order — and that combination differs between Chrome, Firefox, curl and a Python script. Hashing it produces a fingerprint (JA3, or the newer JA4) that reveals the underlying library even when the user-agent header says something else entirely.

How a TLS fingerprint is built

Before any encrypted data flows, the client sends a Client Hello announcing what it supports: TLS versions, cipher suites in preference order, extensions, elliptic curves and point formats. Nothing in there is secret and nothing identifies a person — it describes the software.

JA3 takes several of those fields, joins them in a fixed order and hashes the result into a 32-character MD5 string. Two clients built on the same library and version produce the same hash; a different library produces a different one.

The important property is that this happens before the request. A server knows something about the client before it has seen a single HTTP header — including before it has seen the user-agent that the client might be lying in.

What goes into a JA3 fingerprint
TLSVersion,Ciphers,Extensions,EllipticCurves,CurveFormats

771,4865-4866-4867-49195-49199,0-23-65281-10-11,29-23-24,0
         │                        │                │
         │                        │                └─ curves
         │                        └─ extension list, in order
         └─ cipher suites, in the client's preference order

→ MD5 → cd08e31494f9531f560d64c695473da9

Why JA4 replaced JA3

JA3 has a practical weakness: some fields it hashes are not stable. Browsers began shuffling extension order deliberately (GREASE), which changes the JA3 hash between connections from the same browser and makes it useless for matching.

JA4 addresses that by sorting the fields it hashes, and by producing a human-readable prefix instead of one opaque blob. You can see the TLS version, whether SNI was present and how many ciphers and extensions were offered without looking anything up — which makes it far easier to reason about and to debug.

JA3JA4
OutputOne MD5 hashReadable prefix plus hashes
Extension orderIncluded — breaks with GREASESorted — stable
ReadabilityOpaqueVersion, SNI and counts visible
VariantsJA3S for the server sideJA4H for HTTP, JA4S, JA4L and others

What it actually catches

The classic case is a script claiming to be a browser. A Python request with `User-Agent: Mozilla/5.0 ... Chrome/126` negotiates like Python, because it is Python — its fingerprint matches no browser build in existence. The contradiction between claimed identity and observed handshake is the signal.

It also groups an attack. If one fingerprint is shared by thousands of addresses that all arrived in the last minute and all request the same endpoint, that is one tool being run at scale, regardless of how many networks it comes from.

  • Scripts and libraries impersonating browsers
  • Old or unusual TLS stacks that no current browser ships
  • One tool being run from many addresses simultaneously
  • Clients whose HTTP/2 settings contradict their claimed browser

The limits — and the trap

A fingerprint is shared, not unique. Every Chrome user on the same version and platform produces the same value, which is good for privacy and a hard constraint for detection: a fingerprint identifies a class of software, never an individual.

That leads directly to the most expensive mistake in this field. It is tempting to treat many requests sharing one fingerprint as evidence of a botnet — but legitimate browser traffic is extremely homogeneous, because most visitors run one of a handful of Chrome builds. Real traffic can easily be *more* uniform than the attack you are trying to catch.

Can it be evaded?

Yes, and increasingly easily. Libraries exist specifically to reproduce a browser's Client Hello byte for byte, and a headless browser needs no impersonation at all because it genuinely is a browser.

This does not make fingerprinting useless — it raises the cost of an attack, which is the actual goal — but it does mean the fingerprint must be one input among many. Sound systems combine it with header coherence, behaviour, network reputation and challenge outcomes, and require several to agree before acting.

Frequently asked questions

Last updated