● early access
Fingerprinting

TLS Fingerprinting Explained: JA3, JA4 and the ClientHello

How the TLS ClientHello reveals which software made a request, how JA3 and JA4 turn it into a fingerprint, and what Chrome's extension shuffling changed.

BannedRobots Teampublished 4 min read

Before an HTTPS request sends a single header, the client has already said a lot about itself. The first message of every TLS connection, the ClientHello, lists which cipher suites, extensions and parameters the client supports, in a particular order. That list is decided by the client’s TLS library and its configuration, not by the application code. Changing it takes a lot more effort than changing a User-Agent header.

That’s what makes TLS fingerprinting useful for bot detection. A Python script can claim to be Chrome in its headers, but its handshake still looks like Python.

What’s in a ClientHello

A ClientHello contains, among other things:

  • The TLS version the client offers.
  • The cipher suites it supports, in preference order.
  • Extensions: server name (SNI), ALPN (which HTTP versions it speaks), supported groups (elliptic curves), signature algorithms, key share, session tickets, and more.
  • Parameters inside those extensions: which curves, which signature algorithms, which ALPN values.

Different TLS stacks make different choices. OpenSSL, BoringSSL (Chrome), NSS (Firefox), Go’s crypto/tls, Java and Schannel (Windows) each have characteristic defaults. Even versions of the same library differ. The combination is distinctive enough to identify the client software, and often its version.

JA3: the first widely used standard

JA3 was published by researchers at Salesforce in 2017 and became the de facto standard for TLS client fingerprints. It concatenates five fields from the ClientHello, as decimal values:

SSLVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats

Values within a field are joined with -, fields with ,, and the whole string is hashed with MD5:

771,4865-4866-4867-49195-49199,0-23-65281-10-11-35-16-5-13,29-23-24,0
→ md5 → a 32-character hex fingerprint

JA3 ignores GREASE values (RFC 8701): random-looking placeholder values that browsers insert to stop servers from depending on a fixed list. Without that rule the same browser would produce a different JA3 on every connection.

JA3 worked well for years. Then browsers changed.

Chrome shuffles its extensions

In early 2023, with Chrome 110, Chrome started randomizing the order of TLS extensions in its ClientHello. The goal was the same as GREASE: stop servers and middleboxes from hard-coding assumptions about the exact byte layout of a handshake.

Because JA3 hashes extensions in the order they appear, one Chrome install now produces many different JA3 hashes. JA3 became much less useful for identifying browsers.

Two things are worth noting:

  1. Most bot tooling didn’t follow. Python’s requests, Go’s default HTTP client, curl and most scraping libraries still send a fixed extension order. Their fingerprints stayed stable.
  2. The information is still there. The set of extensions hasn’t changed, only the order. A fingerprint that sorts values before hashing gets its stability back.

JA4: sorting the problem away

JA4, released by FoxIO in 2023 as part of the JA4+ suite, was designed with this in mind. A JA4 fingerprint has three readable parts, a_b_c:

t13d1516h2_8daaf6152771_b186095e22b6
  • a: a readable prefix. Transport (t for TCP, q for QUIC), TLS version (13), whether SNI is a domain (d) or an IP (i), the number of cipher suites (15), the number of extensions (16), and the first and last characters of the first ALPN value (h2).
  • b: a truncated SHA-256 of the cipher suites, sorted.
  • c: a truncated SHA-256 of the extensions, sorted, plus the signature algorithms.

Sorting makes JA4 immune to extension shuffling. The readable prefix also lets you reason about a fingerprint without looking it up, for example “TLS 1.3 over TCP, HTTP/2, 15 ciphers”.

Where to get TLS fingerprints

You don’t need to parse ClientHello messages yourself. TLS fingerprints are increasingly available from the infrastructure in front of your site:

  • CDNs and edge platforms. On Cloudflare, JA3 and JA4 are part of Bot Management, which is an Enterprise add-on. See Cloudflare’s bot products compared. Other CDNs and WAFs offer similar fields.
  • Load balancers and reverse proxies. Several open-source proxies have modules or plugins that compute JA3 or JA4 at TLS termination.
  • Network monitoring tools. Traffic analyzers commonly log JA3/JA4 for security investigations.

Wherever it comes from, store the fingerprint next to your request logs, so you can group traffic by client software as well as by IP.

TLS alone is not enough

A TLS fingerprint identifies a TLS stack, not a client. That has two consequences:

  • Collisions between legitimate and bad traffic. Every script built on the same Python version and OpenSSL build shares one TLS fingerprint, whether it’s a scraper or your own monitoring job.
  • Impersonation is possible. Libraries exist that mimic a browser’s ClientHello (curl-impersonate and uTLS-based tools, for example). They raise the bar, but determined operators do use them.

That’s why production systems combine TLS with other layers: HTTP/2 settings and header patterns, the User-Agent and client hints the client claims, and the network it comes from. Inconsistencies between layers are a strong signal in themselves. A request whose user agent says Chrome 124 but whose handshake matches Python’s ssl module is lying about something. We cover combining these in multi-signal request fingerprints.

Key takeaways

  • The TLS ClientHello describes the client’s TLS library, and it’s much harder to change than headers.
  • JA3 hashes the ClientHello fields in order. Chrome’s extension shuffling (Chrome 110, 2023) broke its stability for browsers, but not for most bot tooling.
  • JA4 sorts ciphers and extensions before hashing and adds a readable prefix, so it survives shuffling.
  • CDNs, proxies and monitoring tools can provide JA3/JA4 for you. On Cloudflare they’re part of Bot Management.
  • Use TLS as one layer of a fingerprint, not the whole fingerprint.
tls fingerprintingja3ja4clienthellocloudflare workers