Skip to content
ProxyRaw
advancedFingerprinting

TLS Fingerprinting (JA3 / JA4)

A passive cryptographic profiling method that identifies client software by hashing the configuration parameters transmitted in the unencrypted TLS Client Hello packet during HTTPS connection setup.

TLS fingerprinting is a powerful, passive network-level technique that identifies the specific software application making an HTTPS connection. First formalized by Salesforce researchers John Althouse, Jeff Atkinson, and Josh Atkins in 2017 with the JA3 standard, TLS fingerprinting allows servers to identify bots before a single byte of HTTP data is decrypted.

Because TLS fingerprinting operates during the initial cryptographic handshake, changing your HTTP User-Agent header or rotating residential proxies has zero effect on your TLS fingerprint.

How TLS Fingerprinting Works Under the Hood (link to this section)

When a client establishes an HTTPS connection to a server, an unencrypted TLS Handshake occurs before encryption begins:

  1. The client sends an unencrypted Client Hello packet containing its cryptographic capabilities:
  • Supported SSL/TLS version
  • List of accepted Cipher Suites (ordered by client preference)
  • List of supported TLS Extensions (e.g., SNI, ALPN, Supported Groups)
  • Supported Elliptic Curves / Groups
  • Elliptic Curve Point Formats
  1. Different programming languages and browsers assemble this packet differently:
  • Google Chrome has a distinct list of ciphers and extensions ordered in a specific way.
  • Python's requests (powered by OpenSSL) sends a completely different set of ciphers.
  • Go's net/http library, Node.js axios, and Java all produce unique, hardcoded Client Hello packets.

The JA3 and JA4 Standards (link to this section)

The JA3 Standard (link to this section)

JA3 concatenates five specific Client Hello fields into a comma-delimited string:

SSLVersion,CipherSuites,Extensions,EllipticCurves,EllipticCurvePointFormats

For example:

771,4865-4866-4867-49195-49199,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0

This string is passed through an MD5 hashing algorithm to generate a 32-character fingerprint (e.g., b32309a26951912be7dba376398abc3b).

The JA4+ Modern Standard (link to this section)

While JA3 was revolutionary, TLS 1.3 introduced GREASE (Generate Random Extensions And Sustain Extensibility), where browsers inject randomized placeholder ciphers to prevent protocol ossification. This causes JA3 hashes to vary randomly on Chrome.

In 2023, John Althouse released JA4+, which filters out GREASE values, sorts extensions, and prefixes the hash with human-readable protocol indicators:

  • t13d1516h2_8daaf6152771_01004bc7a1a0
  • t: TCP protocol (q for QUIC)
  • 13: TLS 1.3 version
  • d: Domain name SNI present
  • 15: Number of cipher suites
  • 16: Number of extensions
  • h2: ALPN protocol (HTTP/2)

Why Python 'requests' and Go 'net/http' Fail (link to this section)

When a developer writes:

python
import requests
# Spoofing Chrome User-Agent header
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..."}
requests.get("https://protected-site.com", headers=headers)

The WAF (Cloudflare, Akamai, DataDome) inspects the incoming connection:

  1. The User-Agent says: "I am Google Chrome on Windows 10."
  2. The TLS JA3/JA4 fingerprint says: "I am Python urllib3 / OpenSSL."
  3. The WAF spots the blatant contradiction and instantly drops the connection with an HTTP 403 Forbidden.

How Modern Scrapers Solve TLS Fingerprinting (link to this section)

To bypass TLS fingerprinting, web scrapers cannot rely on standard HTTP client libraries. They utilize TLS impersonation tools:

  • curl-impersonate: A patched version of cURL that compiles with NSS and BoringSSL to replicate Chrome and Firefox's exact Client Hello packet.
  • tls-client / tls-client-api: Libraries in Python and Go that hook into custom C binaries to send authentic browser TLS signatures.
  • Anti-Detect Browsers: Because they run real Chromium or Firefox engines, their TLS handshakes are 100% authentic.

In practice

  • A Python scraper rotating 100,000 residential proxies gets blocked by Cloudflare on request #1 because its JA3 fingerprint matches Python OpenSSL.
  • Using `curl-impersonate` to scrape an Akamai-protected airline portal by mimicking the exact cipher suite order and ALPN extensions of Chrome 131.
  • An enterprise cybersecurity team cataloging malware command-and-control (C2) servers by identifying unique JA3 hashes of specific botnet binaries.
  • Diagnosing why a Go-based microservice cannot access a third-party API behind AWS CloudFront.

Common use cases

WAF bot mitigation and scraping prevention (Cloudflare, Akamai)Cybersecurity threat hunting and malware identificationWeb scraping protocol emulation (`curl-impersonate`)API reverse-engineering and bot developmentNetwork traffic classification and intrusion detection

Frequently asked

The User-Agent is an HTTP header sent after encryption begins. The TLS Client Hello handshake happens before encryption. WAFs inspect the TLS handshake first; if its cipher signature does not match the declared User-Agent, you are blocked immediately.

JA3 is an MD5 hash of raw Client Hello parameters that can change due to randomized GREASE values in modern browsers. JA4 normalizes parameters, ignores GREASE, sorts extensions, and adds human-readable protocol prefixes for higher accuracy.

You cannot do it using standard `requests` or `axios` because they rely on system OpenSSL libraries. You must use specialized tools like `curl-impersonate`, `tls-client`, or full browser automation.

GREASE (Generate Random Extensions And Sustain Extensibility) is a mechanism where modern browsers inject randomized dummy cipher suites and extensions to ensure servers properly ignore unknown parameters.

Related terms