TLS Fingerprinting Explained: JA3, JA4 and Why Your Scraper Is Blocked Before It Sends a Request
TLS fingerprinting identifies a client by the way it opens an encrypted connection. The first message of every TLS handshake, the ClientHello, lists the protocol versions, cipher suites and extensions the client supports, in an order specific to each TLS library. JA3 and its successor JA4 turn that message into a short fingerprint. A default Python or Node HTTP client produces a fingerprint no browser has, so a site can block it before reading a single header. A proxy does not change this, because the TLS handshake runs end to end through the tunnel.
- tls fingerprinting
- ja3
- ja4
- web scraping
- detection

Table of contents
How the fingerprint is built
Every HTTPS connection starts with a ClientHello. It is sent in the clear, before encryption begins, and it contains the client's supported TLS versions, cipher suites, extensions, elliptic curves and more. Browsers, OpenSSL-based libraries and Go's TLS stack all fill these lists differently, so the message itself identifies the software that sent it.
| JA3 | JA4 | |
|---|---|---|
| Introduced | 2017, Salesforce | 2023, FoxIO |
| Input | Version, ciphers, extensions, curves, point formats | Similar fields, with extensions sorted, plus ALPN and SNI |
| Output | MD5 hash | Readable prefix plus truncated hashes |
| Stable for modern Chrome? | No — Chrome randomises extension order | Yes — sorting removes the randomness |
Chrome started randomising the order of TLS extensions in 2023, which gave the same browser many different JA3 hashes and made JA3 far less useful. JA4 sorts the extensions before hashing, so it stays stable, and its readable prefix — protocol, TLS version, number of ciphers and extensions, ALPN — can be matched partially.
Why proxies do not help
For an HTTPS target, your client asks the proxy to open a tunnel with CONNECT, then performs the TLS handshake with the website through that tunnel. The proxy only moves bytes. The website sees the proxy's IP and your ClientHello. A pool of a million clean residential IPs all presenting the python-requests TLS signature looks like one very large bot.
If requests fail instantly with 403 on a site behind a major bot-management product, while the same URL loads in a normal browser on the same exit, the TLS or HTTP/2 fingerprint is the first thing to test — before blaming the IP.
HTTP/2 fingerprinting
The same idea applies one layer up. HTTP/2 clients send settings, window sizes, priorities and header order at the start of a connection, and these differ between browsers and libraries too. Many HTTP libraries still default to HTTP/1.1, which is itself a signal on sites where every real browser uses HTTP/2 or HTTP/3.
What actually changes your TLS fingerprint
| Approach | Effect | Trade-off |
|---|---|---|
| Real browser (Playwright, Puppeteer) | Genuine browser TLS and HTTP/2 | Heavier, slower, more bandwidth |
| Browser-impersonating HTTP client | Mimics a specific browser's ClientHello | Must track browser updates |
| Changing cipher lists by hand | Produces a new, unusual fingerprint | Rarely matches any real browser |
| Changing proxy or IP | No effect on TLS fingerprint | — |
In Python, curl_cffi can impersonate recent Chrome, Safari and Firefox handshakes while keeping a requests-like API:
from curl_cffi import requests
proxy = "http://USERNAME:PASSWORD@PROXY_HOST:PROXY_PORT"
r = requests.get(
"https://example.com",
impersonate="chrome",
proxies={"http": proxy, "https": proxy},
timeout=30,
)
print(r.status_code)Keep the rest of the request consistent with the browser you impersonate: a Chrome handshake with a Firefox user agent, or with headers in an order no browser sends, contradicts itself. The broader picture of device-side signals is in what is browser fingerprinting.
Clean exits for a clean handshake
Once the client looks like a browser, the IP is the next signal. Residential from €1.20/GB with sticky sessions; ISP from €1.80/IP.
Who uses TLS fingerprints
Bot-management products from the large CDNs expose JA3 and JA4 values to their detection models and, on higher tiers, to site owners' own rules. Rate limits can be counted per JA4 fingerprint instead of per IP, which is why rotating IPs sometimes does nothing against a limit — see Cloudflare 1015. Security teams also use them to spot malware and unusual clients on their own networks.
Sources
Frequently asked questions
What is TLS fingerprinting?
It is a way to identify the software making an HTTPS connection from the first message of the TLS handshake, which lists the client's supported versions, ciphers and extensions in a library-specific way.
What is the difference between JA3 and JA4?
Both turn the TLS ClientHello into a fingerprint. JA3 hashes the fields in the order they are sent, which broke when Chrome began randomising extension order. JA4 sorts extensions first, adds ALPN and SNI information and uses a partly readable format, so it stays stable.
Does a proxy change my TLS fingerprint?
No. For HTTPS the proxy only tunnels the connection, and the TLS handshake takes place between your client and the website. The site sees the proxy's IP but your client's fingerprint.
Why is Python requests blocked when a browser is not?
Python requests uses OpenSSL defaults that produce a TLS and HTTP fingerprint no browser has, so bot-detection systems can identify it before reading any headers. A browser-impersonating client or a real browser avoids this.
How do I change my TLS fingerprint?
Use a real browser through Playwright or Puppeteer, or a client that impersonates a browser handshake, such as curl_cffi in Python. Changing individual ciphers by hand usually creates a new, unusual fingerprint instead.
The seamless team runs residential, ISP and datacenter proxy infrastructure and writes these guides from day-to-day operational experience.
Ready to try seamless proxies?
Residential, ISP and datacenter proxies with no data expiry.
Browse PlansKeep reading
What Is Browser Fingerprinting? How Sites Recognise You Without Cookies
A new IP and a cleared cookie jar still leave the same browser behind. Fingerprinting is how sites notice — and why consistency matters more than uniqueness.
GuidesHow to Avoid Getting Blocked When Web Scraping
The six layers a modern anti-bot system checks, in the order it checks them — and what to change at each one to stay unblocked.
AutomationProxies in Python requests: Sessions, Rotation and the Mistakes That Cost Hours
The proxies dict takes thirty seconds to write and then behaves in four surprising ways. Sessions, encoding, SOCKS DNS and retries are where the hours actually go.
