JA3 and JA4 TLS Fingerprinting Explained (And How to Test Yours)

Published June 4, 2026 · 9 min read

Your scraper sends perfect Chrome headers, runs behind a clean residential IP, and still gets a 403 on the very first request. No captcha, no challenge page, nothing to solve. The site identified you before a single byte of HTTP was exchanged — during the TLS handshake.

This article explains how JA3 and JA4 fingerprinting actually work, why they're the quiet workhorse of every major anti-bot system in 2026, and how to see your own fingerprint in ten seconds. If you want the bypass tooling first, we covered that in Bypass TLS Fingerprinting with curl_cffi & tls-client — this is the companion piece on how the detection side works.

The Ten-Second Test

Before the theory, look at your own handshake. Open our free JA3/JA4 checker — it echoes back the TLS fingerprint your client just presented and tells you what it matches:

# From a terminal - see what curl's TLS looks like:
curl https://check.jibaoproxy.com/api/fingerprint

# Then open the same URL in Chrome and compare.

Run both and you'll see the point of this entire article: curl and Chrome produce completely different fingerprints, no matter what User-Agent header you set.

What Happens in a TLS Handshake

Every HTTPS connection starts with a ClientHello message, sent in cleartext before any encryption begins. In it, your client announces:

Here's the detection insight: these lists and their exact order are baked into the TLS library your client was compiled against. Chrome's BoringSSL announces one specific combination. Python's OpenSSL announces a different one. Go's crypto/tls a third. You cannot change this with headers — it's negotiated below the HTTP layer, by the library itself.

JA3: Hashing the Handshake

JA3 (Salesforce, 2017) concatenates five ClientHello fields — TLS version, ciphers, extensions, curves, point formats — into a string and MD5-hashes it:

771,4865-4866-4867-49195-49199,0-23-65281-10-11-35-16,29-23-24,0
              |
              v  MD5
        cd08e31494f9531f560d64c695473da9

One hash per TLS stack. Every copy of Chrome 137 on Windows produces (nearly) the same JA3; every python-requests on OpenSSL 3.x produces the same different JA3. Defenders maintain lookup tables: hash X = Chrome, hash Y = requests, hash Z = Go bot. If your User-Agent says Chrome but your JA3 says OpenSSL, you're blocked — instantly, silently, cheaply.

JA4: The 2026 Standard

JA3 had problems: Chrome started randomizing extension order in 2023 (breaking naive JA3), and MD5 is unstructured — one hash tells you nothing about why two clients differ. JA4 (FoxIO, 2023) fixed both and is what serious vendors run today:

JA4 = t13d1516h2_8daaf6152771_b0da82dd1658
       |            |             |
       a: protocol  b: ciphers    c: extensions
       (TLS 1.3, 16 ciphers,      (sorted, truncated
        h2 ALPN, domain SNI)       SHA256)

Who Uses This Against You

Every system that matters: Cloudflare exposes JA3/JA4 directly in its bot-management rules; DataDome and PerimeterX feed it into their network-layer scoring (see our DataDome/PerimeterX guide); Akamai has run TLS fingerprinting the longest of anyone. It's popular because it's free to compute, impossible to fake from the HTTP layer, and catches 90% of naive automation — every requests/httpx/axios/Go-http script ever written — before any JavaScript needs to run.

Why a Proxy Alone Doesn't Help

This surprises people: a proxy changes your IP, but your TLS handshake passes through unchanged. The CONNECT tunnel (or SOCKS5 stream) carries your ClientHello bytes verbatim to the target. Residential IP + requests-library JA3 = "a bot running on a residential IP." Better than a datacenter IP, still a bot.

The IP and the TLS fingerprint are independent signals and you need both clean:

IPTLS fingerprintVerdict in 2026
DatacenterLibrary (requests/Go)Blocked everywhere that cares
ResidentialLibraryBlocked on JA4-checking sites
DatacenterBrowser-impersonatedBlocked on IP reputation
ResidentialBrowser-impersonatedPasses network-layer checks

Making Your Fingerprint Match Your Story

Two paths, by tooling:

HTTP clients — use an impersonation library that re-implements a browser's exact ClientHello: curl_cffi (Python), tls-client (Python/Go), got-scraping (Node). Full working code in the curl_cffi guide:

from curl_cffi import requests

r = requests.get(
    "https://target.example",
    impersonate="chrome",
    proxies={"https": "socks5h://USERNAME:[email protected]:913"},
)

Real browsers (Playwright, browser agents, anti-detect browsers) — nothing to do at the TLS layer; the fingerprint is genuinely Chrome's. Your risk lives elsewhere: IP reputation and CDP detection.

Then verify, don't assume. Versions drift — an impersonation library pinned to Chrome 120's handshake reads as "suspiciously outdated browser" in 2026. Re-test after every dependency update:

Free tool · no signup

See your JA3 / JA4 right now

Our checker echoes the TLS fingerprint your client presents, identifies which stack it matches (Chrome / Firefox / OpenSSL / Go), and flags UA-vs-TLS mismatches — the exact check anti-bot systems run.

Test my TLS fingerprint →

Fingerprint clean but still blocked? The other half is IP reputation — test residential IPs with $5 free credit →

Summary

Clean Fingerprint, Clean IP

Pair your impersonated TLS with residential IPs — $5 free credit, no card required.

Start Free Trial
Universal for All IP Products · Massive Nodes Always Available

Join now & enjoy up to 100% deposit bonus.

New users get $5 USDT instantly, plus an extra first-deposit reward — limited-time offer.