Guide9 min read

TLS (JA3/JA4) and TCP Fingerprinting

Short answer

A TLS fingerprint is a short identifier of how a client opens an encrypted connection: the TLS version, cipher suites, extensions and supported groups it offers in its first message, the ClientHello, before any HTTP is sent. JA3 and JA4 are the common formats, and anti-bot systems use them to tell a real Chrome from a Python script that only claims to be Chrome. A proxy does not change it, because the handshake passes through the proxy unchanged; ProxyField's residential proxies change the IP (412M+ IPs from $1.95/GB) while the fingerprint must come from your client, and you can test both with the Easy Trial: 1 GB for $2.95.

residential IPs
412M+
from, traffic never expires
$1.95/GB
Easy Trial
1 GB for $2.95
On this page 10 sections
  1. What is a TLS fingerprint?
  2. How JA3 works
  3. JA4 and JA4+
  4. What Cloudflare's JA4 fingerprint means
  5. TCP/IP fingerprinting
  6. Do proxies or VPNs change your TLS fingerprint?
  7. Why TLS fingerprinting blocks your scraper even with good proxies
  8. How to use curl_cffi with a proxy
  9. HTTP/2 fingerprinting
  10. TLS FAQ

What is a TLS fingerprint?

A TLS fingerprint is a summary of the ClientHello, the first message a client sends when it opens an HTTPS connection. Every TLS library builds that message its own way: which TLS versions it offers, which cipher suites and in what order, which extensions, which elliptic curves. Chrome, Firefox, Safari, curl, Python's ssl module and Go's crypto/tls each produce a recognizable pattern, and the pattern stays the same whatever IP the client uses or which site it visits. Cloudflare describes the fingerprint as "a stable identifier across different destination IPs, ports, and certificates" (Cloudflare: JA3/JA4 fingerprint).

That stability is why it catches scrapers. A script can send a Chrome User-Agent header in one line of code, but its handshake still comes from its own TLS library. When the header says Chrome and the handshake says Python, the request contradicts itself.

How JA3 works

JA3 turns the ClientHello into an MD5 hash. It takes five fields, in this order: the TLS version, the cipher suites, the extensions, the elliptic curves and the point formats; it joins their decimal values into one string and hashes it into 32 characters (salesforce/ja3). JA3S does the same for the server's reply.

JA3 was created at Salesforce in 2017 by John Althouse, Jeff Atkinson and Josh Atkins. The repository was archived on 1 May 2025 and is no longer maintained by Salesforce; the work continues as JA4.

JA3's weak point is that it hashes the extensions in the order they were sent. When Chrome began randomizing the order of its TLS extensions, one Chrome version started to produce many JA3 hashes, which made JA3 less useful for recognizing browsers.

JA4 and JA4+

JA4 fixes that by sorting the ClientHello extensions before hashing, which reduces the number of distinct fingerprints a modern browser produces (Cloudflare: JA3/JA4 fingerprint). It is also readable: the first part of a JA4 string shows the protocol, TLS version, number of ciphers and extensions, so you can tell a lot before you look up the hash.

JA4 belongs to JA4+, a suite of fingerprints by FoxIO (FoxIO-LLC/ja4):

MethodWhat it fingerprints
JA4The TLS client handshake
JA4SThe TLS server response
JA4HThe HTTP client (headers and their order)
JA4TThe TCP client (the connection's SYN packet)
JA4LLatency between client and server
JA4XX.509 certificates

JA4 itself is open source under the BSD 3-Clause license. The other JA4+ methods are licensed under the FoxIO License 1.1, which the README says is not permissive for monetization.

Easy Trial: 1 GB for $2.95.
Try for the price of 1 GB, no subscription, traffic never expires.

What Cloudflare's JA4 fingerprint means

On Cloudflare, a JA4 fingerprint is a property of each HTTPS request that site owners on Enterprise Bot Management can see and act on. Cloudflare's documentation says JA3 and JA4 fingerprints "are only available to Enterprise customers who have purchased Bot Management" (Cloudflare). Site owners can:

  • view the fingerprints in Bot Analytics, Security Events and logs;
  • write WAF custom rules that block, challenge or skip a fingerprint;
  • use JA4 Signals in Workers, which summarize how a fingerprint behaves across Cloudflare's traffic.

The same page notes the limits: there is no fingerprint for plain HTTP traffic, and with TLS session resumption no new fingerprint is calculated after the first handshake. Cloudflare also lists JA3/JA4 among the inputs of its bot detection, next to the bot score; see why Cloudflare thinks you are a bot.

TCP/IP fingerprinting

TCP/IP fingerprinting identifies the operating system that opened a connection from the first packets, without any code on the client. The SYN packet that starts a TCP connection carries values that each OS sets its own way: the initial TTL, the window size, the maximum segment size (MSS) and the order of TCP options. Tools such as p0f read these passively, and JA4T turns them into a fingerprint.

Through a proxy, the TCP fingerprint is not yours. The site's TCP connection comes from the proxy's exit, so the site sees that machine's network stack. This is how TCP fingerprinting can point to a proxy: if the User-Agent and the TLS handshake say Windows and Chrome but the TCP stack looks like a Linux server, the request does not add up. How much weight each anti-bot system gives this is not published.

Do proxies or VPNs change your TLS fingerprint?

No. An HTTP proxy that uses the CONNECT method, and a SOCKS5 proxy, pass your encrypted bytes through without reading them, so the site sees your client's ClientHello exactly as your client sent it. The proxy changes the IP and the TCP connection, not the TLS layer.

LayerWhat the site sees through an HTTP CONNECT or SOCKS5 proxyWho controls it
IP addressThe proxy exit's IPThe proxy
TCP/IP (TTL, window, MSS, options)The proxy exit's network stackThe proxy
TLS ClientHello (JA3/JA4)Your client's handshake, unchangedYour client
HTTP/2 settings, header order (JA4H)Your client'sYour client
User-Agent and other headersYour client'sYour client
Browser signals (canvas, fonts, screen)Your browser'sYour browser

A VPN forwards your packets, so the TLS fingerprint and most of the TCP fingerprint stay those of your device; the tunnel can still show, for example in a smaller MSS. The one exception is a proxy that terminates TLS itself and opens a new connection to the site: then the site sees the proxy's TLS library, and you must trust the proxy with your decrypted traffic.

Why TLS fingerprinting blocks your scraper even with good proxies

Clean residential IPs fix reputation-based blocks; they cannot fix a handshake that gives your client away. The typical failure looks like this:

  • The same URL loads in Chrome on your own connection, but returns 403 or a challenge to your script, even through residential IPs.
  • Changing the User-Agent and adding browser headers makes no difference, or makes it worse.
  • A real browser driven by Playwright through the same proxy passes.

Each of those points to the TLS (or HTTP/2) layer. The fix is to make the handshake match the browser you claim to be, which there are two ways to do:

  1. Use a real browser

    Playwright, Puppeteer or Selenium drive an actual Chrome or Firefox, so the handshake, HTTP/2 settings and headers are genuine. It costs more CPU and memory per page.

  2. Use an HTTP client that impersonates a browser

    Curl_cffi is a Python binding to a curl fork that "can impersonate browsers' TLS/JA3 and HTTP/2 fingerprints"; it is MIT-licensed and supports HTTP and SOCKS proxies (lexiforest/curl_cffi). It is fast, but it runs no JavaScript.

Match the rest too: if you impersonate Chrome, send Chrome's headers in Chrome's order, and keep cookies within the session. Headers are covered in HTTP headers for web scraping.

How to use curl_cffi with a proxy

This script sends one request with Chrome's TLS and HTTP/2 fingerprint through the ProxyField gateway and prints what a TLS echo service saw. Install it with pip install curl_cffi, then replace <login> and <password>.

Python
from curl_cffi import requests

proxy = "http://<login>-country-us-city-new_york:<password>@gate.proxyfield.io:7000"

r = requests.get(
    "https://tls.browserleaks.com/json",
    impersonate="chrome",          # the latest Chrome profile the library ships
    proxies={"http": proxy, "https": proxy},
    timeout=30,
)
data = r.json()
print("status:", r.status_code)
print("ja3:", data.get("ja3_hash"))
print("ja4:", data.get("ja4"))
print("user agent:", data.get("user_agent"))

Run the same request with plain requests and compare: the IP stays the proxy's in both cases, while the JA3 and JA4 values change with the client. To keep one IP for a whole session, use a sticky session (30 s to 36 h): -region-<region>, -session-<id> and -ttl-<seconds> (30 to 129,600 seconds, 20 minutes by default) after the location, as in <login>-country-us-region-new_york-city-new_york-session-he9f2qga-ttl-600. The Python setup for the gateway in general is on the Python proxy integration page.

HTTP/2 fingerprinting

HTTP/2 adds a second fingerprint above TLS. Browsers open an HTTP/2 connection with their own SETTINGS values, window sizes, priority behavior and pseudo-header order, and libraries differ from them just as they do in TLS. JA4H covers the HTTP side, including header order. An impersonating client such as curl_cffi matches HTTP/2 as well as TLS; a client that matches only TLS can still be caught here.

Proxies for the IP layer, any client on top. ProxyField residential proxies work with real browsers and impersonating clients over HTTP, HTTPS and SOCKS5, with per-request rotation or sticky sessions up to 36 h. From $1.95/GB, no subscription; start with the Easy Trial (1 GB). Traffic never expires, and all purchases are final.

TLS FAQ

What is TLS fingerprinting, and why does it block my scraper even with good proxies?

ProxyField's 412M+ residential IPs change the IP, not the TLS fingerprint, which is why good proxies alone don't stop it from blocking a scraper. TLS fingerprinting identifies your client from the way it opens an HTTPS connection, and a proxy passes that handshake through unchanged, so a clean IP can't hide a Python handshake behind a Chrome User-Agent. Use a real browser or a client that impersonates one, such as curl_cffi, and keep the proxy (from $1.95/GB) for the IP side.

What is a TLS fingerprint?

It is an identifier built from the ClientHello, the first message of a TLS handshake: the TLS versions, cipher suites, extensions and curves a client offers. Each browser and library has its own pattern, and it stays the same across IPs and sites.

What is a JA3 fingerprint?

JA3 is a TLS client fingerprint created at Salesforce in 2017: five ClientHello fields (version, ciphers, extensions, curves, point formats) joined into a string and hashed with MD5. Its repository was archived in May 2025, and JA4 has largely replaced it.

What is a JA4 fingerprint?

JA4 is the successor to JA3 from FoxIO. It sorts the TLS extensions before hashing, so browsers that shuffle their extension order keep one fingerprint, and its first part is readable (protocol, TLS version, cipher and extension counts).

What does Cloudflare's JA4 fingerprint mean?

It is the JA4 value Cloudflare computes for each HTTPS request, which site owners with Enterprise Bot Management can see in their analytics and use in WAF rules to block, challenge or allow a client. It identifies the TLS client software, not the person or the IP.

Do proxies or VPNs change TLS fingerprints like JA3 and JA4?

No. HTTP CONNECT and SOCKS5 proxies pass the encrypted handshake through, and a VPN forwards your packets, so the site sees your client's JA3 and JA4. Only a proxy that terminates TLS and reconnects would show its own fingerprint instead.

How does TCP fingerprinting detect proxies?

The site reads the operating system from the first TCP packet (TTL, window size, MSS, option order), and through a proxy that packet comes from the proxy's exit. If it looks like a different OS from the one the browser and TLS handshake claim, the mismatch can count against the request.

Whose TCP fingerprint does a website see through a proxy?

The proxy exit's, because the exit opens the TCP connection to the website. Your own TCP stack only talks to the proxy.

What is the difference between JA4 and JA4T?

JA4 fingerprints the TLS ClientHello, so it identifies the client software; JA4T fingerprints the TCP SYN packet, so it points to the operating system and network stack that opened the connection. Through a proxy, JA4 is yours and JA4T is the proxy exit's.

How do I use curl_cffi for web scraping?

Install it with pip install curl_cffi, then call requests.get(url, impersonate="chrome", proxies={...}) from curl_cffi; the API mirrors Python requests. It matches Chrome's TLS and HTTP/2 fingerprints but runs no JavaScript, so pages behind a JavaScript challenge still need a real browser.

Show 2 more questionsShow fewer questions

What is a proxy fingerprint?

The signals that reveal a request came through a proxy: the IP's network type and reputation, a TCP stack that doesn't match the claimed OS, a time zone or language that doesn't match the IP's country, and headers some proxies add. Residential and mobile IPs avoid the data center signal; the rest depends on your client.

Can passive OS fingerprinting identify an exact operating system?

Usually only the family and sometimes a version range, not an exact build. TTL, window size and TCP options are shared by many systems, and middleboxes such as NAT routers and proxies can change them.

Easy Trial: 1 GB for $2.95.

Try for the price of 1 GB, no subscription, traffic never expires.