Glossary

What Is a TLS Fingerprint (JA3 / JA4)?

A TLS fingerprint is a hash derived from the specific structure of a client's TLS handshake (cipher list, extensions, ALPN values, GREASE bytes, etc.) that identifies the underlying HTTP client, browser, or library before any application-layer data is exchanged.

Understand how JA3 and JA4 work, why they reveal that you're using Python requests or curl before your scraper even sends an HTTP request, and how to neutralize them.

Explained

When your client opens a TLS connection, the very first bytes are a ClientHello packet describing what cipher suites, TLS versions, extensions, elliptic curves, and ALPN values your client supports. The exact list and ordering of these attributes is implementation-specific — Chrome's ClientHello looks different from Firefox's, which looks different from curl's, which looks different from Python's `requests` library, and so on.

JA3 (and its successor JA4) is a hash format that turns the structure of the ClientHello into a short identifier. Anti-bot vendors compute this hash for every incoming TLS connection and check it against known signatures. If your scraper uses Python `requests`, your TLS fingerprint matches the OpenSSL default and is instantly identifiable as 'not a real browser' — even before you send a single HTTP byte.

This is why so many scrapers fail on Cloudflare, Akamai, and similar stacks even when the IP and User-Agent look correct. The TLS layer gives away that the request didn't come from Chrome. Modern stealth libraries (like `curl_cffi`, `tls-client`, Playwright with the right launch flags) impersonate real browser TLS fingerprints to avoid this.

How It Works

JA3 builds the fingerprint from five fields of the ClientHello: TLS version, supported cipher suites, supported extensions, supported elliptic curves, and supported elliptic curve point formats. It joins them, MD5-hashes the result, and produces a 32-character signature.

JA4 (the modern replacement) extends this with ALPN, version, SNI presence, GREASE handling, and orders extensions in a stable way that's resilient to randomization. JA4 also has variants for QUIC (JA4Q), HTTP (JA4H), and SSL session (JA4S). Servers compute the fingerprint and either look it up against allow/deny lists or feed it into a risk-scoring model alongside other signals.

Types

JA3

Original TLS ClientHello fingerprint, MD5 of ordered cipher / extension / curve / format fields. Widely deployed but vulnerable to extension-order randomization in modern browsers.

JA3S

Server-side counterpart of JA3, fingerprints the TLS ServerHello. Used to identify the server software stack rather than the client.

JA4

Modern successor to JA3. Handles GREASE, randomization, and extension ordering more robustly. Splits into JA4 (TCP), JA4Q (QUIC), JA4H (HTTP), JA4L (latency), JA4T (TCP fingerprint), JA4X (X.509 cert).

Akamai BMP / Datadome / Cloudflare bot.management

Proprietary anti-bot fingerprints that build on top of JA3/JA4 with additional signals (TLS extension order randomization quirks, GREASE byte inspection, packet timing). Effectively impossible to fake without using a real browser.

Common Use Cases

Identifying HTTP clients (curl, requests, Python http.client) before any HTTP layer
Bot defense at the TLS layer
Network forensics and IDS rules
Rate-limiting by client type
Detecting custom or modified scraping clients
FAQ

Frequently asked FAQ questions

Common questions about tls fingerprint.

User-Agent is an HTTP header your client sends in plaintext; you can set it to anything. JA3 is computed from the structure of the TLS handshake itself — you can't change it just by setting a header. To change your JA3 you have to change the underlying TLS client library or its configuration.