Glossary

Detection & reputation

TLS fingerprint

An identifier built from how a client negotiates encryption, used to spot automation even through a clean IP.

Before a single byte of your request is encrypted, your client has already introduced itself. The opening message of a TLS handshake, the ClientHello, is sent in the clear, and it is a detailed declaration: which TLS versions you support, which cipher suites and in what order of preference, which extensions you implement and in what order, which elliptic curves you accept, which point formats. None of it is secret, and all of it is read before any encryption begins.

That declaration is not standardised down to the ordering, which is precisely what makes it identifying. Chrome offers one set in one order. Firefox offers a different set in a different order. Python's requests library, Go's net/http, curl and a headless browser each have their own. Hash the whole arrangement into a short string and you have a fingerprint that says which software is talking, regardless of what that software later claims about itself.

JA3 was the first widely adopted version of this, an MD5 hash over the TLS version, cipher list, extension list, elliptic curves and curve point formats joined together. JA4 is its successor and is the one worth knowing now: it is more resistant to the cipher-shuffling that let clients evade JA3, and it is structured and partly human-readable rather than an opaque hash, which makes it far more useful to defenders.

The reason this matters more than almost any other signal is where it sits. It is below your IP address, so a perfect residential proxy does not touch it. It is below your headers, so a flawless User-Agent string does not touch it either. Send a request from a trusted German household address, announce yourself as Chrome 130 on Windows, and hand over a Python TLS handshake, and you have produced a contradiction that costs one lookup to detect and that no amount of proxy quality can paper over.

This is the specific reason a scraper that works perfectly in a browser fails from a script against the same target with the same proxy. People conclude the proxy is bad and buy a more expensive one. The proxy was never the problem. The handshake was.

Fixing it means changing what your client actually sends, not what it claims. That is why libraries exist specifically to reproduce a real browser's handshake byte for byte, uTLS being the well-known one in Go, and why driving a real browser sidesteps the issue entirely by genuinely being the thing it claims to be.

One caution: a fingerprint is only a contradiction if it disagrees with the rest of your story. A well-known library handshake is not automatically suspicious on an API that expects scripts. It becomes suspicious when it arrives dressed as a consumer browser on a consumer address, because real consumer browsers do not do that.

How HProxy handles it

This is exactly why we say the address is one layer and not the whole answer. A trusted IP raises your floor; it cannot rescue a client that identifies itself as automation before the encryption starts. Anyone debugging a block should check the handshake before buying a more expensive proxy.

Frequently asked questions

What is the difference between JA3 and JA4?

JA3 is the original TLS fingerprint: an MD5 hash over the TLS version, cipher suites, extensions, elliptic curves and curve point formats from the ClientHello. Its weakness is that clients could shuffle or pad those lists to produce a different hash and evade matching. JA4 is the successor, designed to resist that shuffling, and it is structured and partly readable rather than a single opaque hash, so a defender can reason about parts of it instead of only comparing it to a known list.

Can a proxy hide my TLS fingerprint?

No. An ordinary proxy relays your connection, so the ClientHello your client generates is the one that reaches the destination. The proxy changes which IP address the handshake appears to come from and changes nothing about the handshake itself. Changing the fingerprint requires changing the client that produces it.

Why does my scraper work in a browser but fail from a script?

Usually because the browser and the script have different TLS fingerprints even though everything else about the request matches. The script can copy the browser's headers, cookies and User-Agent exactly and still announce a completely different cipher and extension ordering at the TLS layer, which a detection system reads as automation before it evaluates anything else. It is the most common cause of a block that survives changing proxies.

How do I check my own TLS fingerprint?

Send a request through the client you are worried about to any service that echoes back the JA3 or JA4 value it observed, then repeat the same request from a real browser and compare. If the two differ, that difference is visible to every site you contact, and it is visible before your request is decrypted.

Does using HTTP/2 change the fingerprint?

It adds another one. Beyond the TLS handshake, HTTP/2 clients differ in their settings frame values, header ordering and priority behaviour, which produces a separate HTTP/2 fingerprint alongside the TLS one. A client can match a browser at the TLS layer and still be identifiable a layer above it.

Back to the full glossary.

HProxy.

Ready when you are.Your dashboard is ten seconds away.

Get Startedor talk to us at support@hproxy.com
HProxy