← All dispatches
Dispatches · #intelligence · DLBrowser

TLS Fingerprinting Outs Your Bot Before HTML Loads

July 12, 2026 · Abhishek Gupta
Infographic: a TLS handshake fingerprint (JA4) identifies a scraper as a bot before any HTML is requested, showing the 2023 Chrome 110 change and the X25519MLKEM768 post-quantum key share as detection signals

Most teams that fight bot detection spend their time on the wrong layer. They patch the JavaScript fingerprint — navigator.webdriver, canvas noise, WebGL vendor strings — and never notice that the site decided they were a bot before a single line of JavaScript ran. TLS fingerprinting makes that call during the handshake, in the first few packets, before the server has sent one byte of HTML.

That is the uncomfortable part. Your headless browser can render like Chrome, click like a human, and still get scored as automation because its network handshake has the wrong shape.

The short version

  • TLS fingerprinting reads the ClientHello — the first message your client sends — and hashes its cipher suites, extensions, and supported groups into a signature that identifies the software, not the person.
  • JA3, the 2017 standard from Salesforce, stopped being reliable after Chrome 110 (February 2023) randomized the order of TLS extensions in every ClientHello, so real Chrome produces a different JA3 hash on every connection.
  • JA4, released by FoxIO in 2023, fixed this by sorting the extensions before hashing — which is why Cloudflare, Akamai, and DataDome moved to JA4 and JA4H as primary signals.
  • The newest tell is post-quantum: Chrome 131 (November 2024) offers the X25519MLKEM768 hybrid key exchange by default. A bare Python or Go client that skips it now looks nothing like the browser it claims to be.
  • Anti-bot systems also compare the TLS handshake against the HTTP request. When the handshake says Chrome but the header order says Python requests, the mismatch is the giveaway.

What is TLS fingerprinting in bot detection?

TLS fingerprinting is a method of identifying client software from the details of its TLS handshake — the cipher suites it offers, the extensions it lists, the elliptic curves it supports, and the order of all of them. Anti-bot systems hash these into a signature (JA3 or JA4) and match it against known browsers. A scraper whose handshake does not match real Chrome is flagged as a bot.

The signal is powerful because it sits below your control surface. You can rewrite headers, spoof User-Agent, and inject stealth patches into the DOM, but the TLS layer is usually handled by whatever HTTP library you imported. Python's requests uses OpenSSL. Go uses its own crypto/tls stack. Node uses BoringSSL but with a different configuration than Chrome ships. Each produces a handshake that a fingerprinter can tell apart from a browser at a glance.

Why JA3 died and JA4 replaced it

JA3 concatenated five fields from the ClientHello — version, cipher suites, extensions, elliptic curves, and curve formats — and MD5-hashed the string. For years, one JA3 hash reliably meant one piece of software.

Then Chrome 110 shipped TLS ClientHello extension permutation in February 2023. Chrome began shuffling the order of its extensions on every connection, on purpose, to stop servers from depending on a fixed order. The side effect: real Chrome now generates a different JA3 on every handshake. Any anti-bot rule that matched a static Chrome JA3 started throwing false positives against actual Chrome users.

FoxIO's JA4 answered this by sorting the extension and cipher lists before hashing, so a reordered-but-identical ClientHello collapses back to the same fingerprint. JA4 is also human-readable rather than a raw MD5 — it encodes the TLS version, SNI presence, cipher count, and ALPN in fields you can read. That readability is why it spread fast across vendors.

SignalJA3 (2017)JA4 (2023)
Hash input orderPreserves ClientHello orderSorts ciphers and extensions
Chrome 110+ randomizationBreaks — new hash per connectionSurvives — stable hash
FormatSingle MD5 stringReadable, multi-part (JA4, JA4H, JA4S)
HTTP-layer variantNoneJA4H fingerprints header order

The post-quantum handshake is the 2025 tell

Here is the counterintuitive one. The strongest fresh signal for spotting a fake browser is not something scrapers added — it is something real browsers added, and scrapers forgot to copy.

In November 2024, Chrome 131 turned on X25519MLKEM768 by default: a hybrid key exchange that runs the classical X25519 curve alongside ML-KEM-768, the lattice-based post-quantum algorithm NIST standardized as FIPS 203 in August 2024. It carries the IANA codepoint 0x11EC, and every current Chromium browser — Chrome, Edge, Brave, Opera — offers it in the ClientHello.

A modern real browser advertises that post-quantum group. A stock HTTP client built on an older TLS stack does not. So an anti-bot engine can ask a simple question: this handshake claims to be Chrome 131 — does it offer the key share Chrome 131 actually sends? If not, the User-Agent is lying, and the request gets scored accordingly. The fingerprint got harder to fake precisely because it now includes cryptography that most scraping libraries have not implemented.

Matching the browser means matching the whole stack

The practical lesson is that beating TLS fingerprinting is not a patch — it is an architecture decision. You cannot bolt a correct handshake onto Python requests; the handshake is produced by the TLS library, and the TLS library is baked into the runtime. Tools like curl-impersonate exist exactly because the only way to reproduce Chrome's handshake is to compile against the same BoringSSL configuration Chrome uses and offer the same ciphers, extensions, and groups in the same shape.

This is where the network fingerprint and the browser fingerprint have to agree. A stealth browser that presents a perfect JavaScript surface but a Go-flavored TLS handshake is more suspicious than an honest one, because the contradiction is itself a signal — a "wrong-shape Chrome." Coherence across every layer, from the ClientHello to the header order to the rendered DOM, is what actually passes. That coherence is the problem DLBrowser is built to solve: a runtime where the TLS handshake, the HTTP/2 frame order, and the browser surface all describe the same real client, because they come from one. For teams weighing where reliability breaks in a data pipeline, our research notes map the failure points.

The takeaway is narrow and it is durable. Detection has moved down the stack, from the page to the packet, and it will keep moving. The teams that keep reaching blocked pages are the ones who stopped treating the browser as a rendering engine and started treating it as a network identity — every layer telling the same true story.

Frequently Asked Questions

What is TLS fingerprinting and how does it detect bots? TLS fingerprinting reads the ClientHello message a client sends when opening an HTTPS connection and hashes its cipher suites, extensions, and supported groups into a signature like JA3 or JA4. Anti-bot systems match that signature against real browsers; a handshake that does not match Chrome or Firefox is flagged as automation before any HTML loads.

Why did JA3 fingerprinting stop working? Chrome 110, released in February 2023, began randomizing the order of TLS extensions in every ClientHello. Because JA3 hashed the extensions in their original order, real Chrome started producing a different JA3 on every connection, making static JA3 rules unreliable. JA4 fixed this by sorting the fields before hashing.

Can you bypass TLS fingerprinting with a proxy? No. A proxy changes your IP address, not your TLS handshake. The fingerprint is generated by your HTTP or TLS library and travels with the request regardless of which proxy it passes through. To match a real browser you must reproduce its actual handshake — its ciphers, extensions, and key shares — not just route traffic through a different address.

What is the X25519MLKEM768 signal in bot detection? X25519MLKEM768 is a hybrid post-quantum key exchange that Chrome 131 offers by default since November 2024. Real Chromium browsers advertise it in the ClientHello; most stock scraping libraries do not. Anti-bot engines use its absence to catch clients claiming to be a modern browser while sending an outdated handshake.

Abhishek Gupta is Co-Founder at Dekrypt Labs, building DLBrowser — a stealth browser runtime for real-world data collection. dekryptlabs.com