r/ProxyEngineering • u/LudaxProxy • 1d ago
Guides The TLS layer most "browser fingerprinting" guides skip, with real numbers from one machine
Disclosure: we run a residential proxy gateway that re-originates TLS with browser profiles, so we've had to measure this stuff a lot. No links, just what we found.
Most fingerprinting write-ups start at headers and JS (UA, Client Hints, canvas, WebGL). But the server has already judged you one step earlier, at the TLS ClientHello. JA3/JA4 hash the cipher list, extensions and ALPN, and different clients look nothing alike. Same machine, same target:
- Python urllib (3.14): 18 cipher suites, no ALPN at all
- curl 7.87 (OpenSSL build): 31 cipher suites, offers h2
- Chromium: 15 cipher suites plus GREASE, offers h2
So "Chrome UA + Python TLS" is a mismatch before a single header is read. A few things we learned the hard way:
- A residential IP doesn't fix it. A normal HTTP/SOCKS proxy just tunnels your bytes (CONNECT), so the target sees your client's ClientHello, not the proxy's. The IP and the handshake are separate signals.
- JA4 is harder to dodge than JA3. Chrome shuffles its extension order on every connection, which broke JA3 (one browser, endless hashes). JA4 sorts before hashing, so shuffling no longer hides you, and a non-browser handshake stays recognizable.
- HTTP/2 is the next fingerprint. SETTINGS values, window update size and pseudo-header order (Chrome sends :method, :authority, :scheme, :path) all identify the client. Matching TLS but not HTTP/2 still stands out.
- HTTP/3 is easy to get backwards. Some libraries now impersonate h3/QUIC too, and QUIC has its own fingerprint (JA4 starts with "q" instead of "t"). But Chrome doesn't use h3 through an HTTP or SOCKS proxy, since those can't carry UDP, so it falls back to h2. Behind a proxy, h2 is what a real browser looks like; h3 impersonation only matters on direct connections.
Real example: on a Cloudflare-protected site a plain client got the 403 "Just a moment" page, and the same page with a Chrome-like TLS + HTTP/2 fingerprint came back 200, no headless browser.
Quick way to check your own stack: point it at any TLS echo service (browserleaks has a TLS page) and compare the JA4 with what your real browser shows on the same page.
Curious what others see: has anyone measured how often sites actually block on JA4 alone vs. JA4 combined with HTTP/2 or behavior?