TLS fingerprinting spots bots by the shape of their handshake. JA3 and JA4 catch scrapers that user-agent checks miss.
What is TLS fingerprinting?
TLS fingerprinting names the software behind an HTTPS connection. It reads the first message of the handshake. When a client opens a TLS connection, it sends a Client Hello packet. That packet announces the TLS version it supports. It lists the cipher suites it prefers. It lists the extensions it understands. It sends them in a set order. That combination is a byte-level signature of the TLS library. Different libraries produce different signatures.
Here is the useful part. The Client Hello is a low-level detail, not a header the scraper picked. You can put any string in the User-Agent field. You cannot easily change the cipher list your TLS library ships with. A Python script that claims to be Chrome gets caught the moment the handshake hits the wire.
Why TLS fingerprinting matters right now
Automated traffic is now most of the web. Automated traffic reached 51% of all web traffic in 2024. That was a first in a decade. Bad bots made up 37%. A site that cannot tell a library from a browser is blind to more than a third of its traffic. That blindness costs the most where scrapers aim: login endpoints, pricing pages, search APIs, and RSS feeds.
The shift from human to agent traffic pushes the question down to the protocol layer. Headers lie. IPs rotate. User agents are a free-text field. The TLS handshake is the one thing the scraper must produce first. It comes before any HTTP body. The code that makes it is the code the scraper runs.
Types of TLS fingerprints (JA3, JA4, JA4H)
Three fingerprint formats lead the field as of 2026.
JA3. This was the first widely used TLS fingerprinting method. Salesforce engineers published it in January 2019. JA3 takes five fields from the Client Hello: version, accepted ciphers, extension list, elliptic curves, and elliptic curve point formats. It joins them together. Then it MD5-hashes the result into a 32-character fingerprint. The input looks like 769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0. The hash output is ada70206e40642a3e4461f35503241d5.
The method worked for years. Then in early 2023, Chrome started to randomize the order of TLS extensions in its Client Hello. The math is brutal. One Chrome client can send 16 extensions in random order. That yields 16 factorial orderings. That is roughly 20.9 trillion distinct JA3 hashes from the same browser on the same machine. Security researchers said JA3 was rendered useless for identifying clients and user agents. Firefox added similar randomization.
JA4 and the JA4+ suite
JA4. FoxIO released JA4 in September 2023 to fix the randomization problem head-on. The fix is simple. Sort the ciphers and extensions before hashing. Random order no longer changes the output. The output never depended on order in the first place. JA4 also splits the fingerprint into an a_b_c format. Each of the three segments can be queried on its own. The a segment holds protocol metadata. The b segment is a truncated SHA-256 of the sorted cipher list. The c segment is a truncated SHA-256 of sorted extensions plus signature algorithms.
JA4+. JA4 is one member of a larger suite. JA4+ adds JA4S for TLS servers, JA4H for HTTP request patterns, JA4X for X.509 certificate generation, JA4T for TCP stack fingerprints, and JA4SSH for SSH connections. Each captures a different layer of the connection. The segments are built to compose. By early 2025, JA4 had shipped in major bot-management products.
The FoxIO team calls out one caveat in the spec. A browser's JA4 shifts about once a year as its TLS library updates. Fingerprint databases must track those updates. If they do not, they build up false positives against the latest Chrome.
How TLS fingerprinting works
Every browser, scraping library, and bot framework shows a different mix of ciphers and extensions. Python's requests library has one JA3 hash. curl has another. Firefox has its own. A scraper can send Mozilla/5.0... in the User-Agent. If it makes Python's JA3 on the wire, it has already told you it is lying.
The match happens before any HTTP data moves. The client opens a TCP connection. It sends the Client Hello as the first TLS message. The edge captures the byte pattern. It hashes or sorts it by JA3 or JA4 rules. Then it looks the hash up in a signature database. If the fingerprint does not match the declared user agent, the edge must decide before the HTTP request starts.
How to identify bots via TLS fingerprints
Numbers show the scale of this work best. We analyze over 15 million unique JA4 fingerprints per day. That runs across more than 500 million user agents and billions of IP addresses. That is the signal set we pull from each day.
The useful part is what the data says about what is not a browser. Out-of-the-box HTTP libraries give themselves away. Python's requests, Go's net/http, and Node's axios all send TLS handshakes no real browser would send. The cipher order is different. The extension set is different. GREASE values are missing. GREASE is the set of intentionally-invalid bytes Chrome injects to keep middleboxes honest. Session ticket behavior is different. You do not need machine learning to catch this. A lookup against a few known library fingerprints flags the first packet.
Two checks surface most library traffic in practice. Match the Client Hello's cipher order against the published order for the declared browser. Match the presence of GREASE values against what that browser version sends. A mismatch on either is a signal. The request is not what it claims to be.
How to respond when bots spoof fingerprints
Scrapers saw JA3 coming years ago. A small industry of impersonation libraries now exists. They replay real browser handshakes from scripts. curl-impersonate is a fork of curl built against NSS and BoringSSL. It produces byte-identical Client Hellos for recent Chrome, Firefox, Edge, and Safari. uTLS is a Go fork of crypto/tls that exposes Client Hello construction. CycleTLS brings the same power to Node.js. curl_cffi gives Python the curl-impersonate handshake. It drops in for the requests library. All four are open source. All four are easy to install.
A 2025 UC Davis study tested 20 commercial bot services against two leading detection systems. It measured average evasion rates of 52.93% against a leading commercial detection system and 44.56% against a widely used detection library. That ran across more than half a million requests (Venugopalan et al., IMC 2025). Call it what it is. This is peer-reviewed data. Over half of commercial scraping traffic gets through one of the most-deployed bot detection systems in the industry.
The response is layered, not lateral. The engineers who build these systems say so plainly. Fingerprints can be spoofed. They change often. Traffic patterns keep evolving. The answer they pair with JA4 is behavioral. They compute inter-request features across the last hour of global traffic. They look for clusters that no honest browser would form.
A single JA4 hash tells you what software made the connection. It does not tell you what that software is doing. Picture a forged Chrome fingerprint. It comes from thousands of residential IPs. It hits hundreds of pages per minute per session. It requests only HTML and skips every image. That is still a bot. Not because of its JA4. The fingerprint narrows the search. Behavior confirms the verdict. Remove any single layer and effectiveness drops.
Key takeaways
- TLS fingerprinting names the software behind an HTTPS connection. It reads the Client Hello before any HTTP data moves. A
User-Agentis a string the scraper chose. A TLS fingerprint is a property of the code that runs. - Three formats cover the field: JA3 (broken by Chrome's 2023 extension randomization), JA4 (sorts before hashing, resistant to randomization), and the JA4+ suite (HTTP, TCP, SSH extensions).
- Fingerprints alone do not close the gap. A UC Davis 2025 study measured average evasion rates of 52.93% against a leading commercial detection system and 44.56% against a widely used detection library using impersonation libraries like curl-impersonate and uTLS.
- The durable stack is layered: TLS fingerprint plus HTTP/2 SETTINGS, behavioral patterns, and a crawler signature database. Centinel runs 1,600+ crawler fingerprints against this layered model at the edge.
