January 2026
Connection Fingerprinting Beyond User-Agent
If you run a web server, you've seen the zoo of User-Agent strings claiming to be Chrome. The string is self-reported and trivially spoofed. But before a single HTTP header is sent, the client has already transmitted identifying information across three protocol layers.
Layer 1: TCP SYN
The very first packet contains initial window size, MSS, and TCP options in a specific order. Set by the OS kernel, not the application. The combination creates a JA4T fingerprint: 64240_2-4-8-1-3_1460_8 for typical Linux, entirely different for Windows.
Layer 2: TLS ClientHello
Cipher suites, extensions, curves, and ALPN — all in an order characteristic of the TLS library. OpenSSL, BoringSSL, NSS, and SecureTransport each produce distinct fingerprints regardless of User-Agent.
Layer 3: HTTP/2 Negotiation
SETTINGS frames and pseudo-header ordering are hardcoded per implementation. Chrome: m,a,s,p. Firefox: m,p,a,s. Go: a,m,p,s. Curl: m,p,s,a. Not configurable through normal APIs.
Cross-Layer Correlation
If User-Agent claims Chrome but JA4 says Python/OpenSSL, JA4T says Linux, and HTTP/2 pseudo-headers say Go — three independent layers all contradict the claim. Spoofing all three simultaneously and consistently is extraordinarily difficult.