Traces GREASE (RFC 8701), the reserved random values browsers inject into the TLS ClientHello to keep extension points usable, and the reason fingerprinting that fails to normalize them produces a different hash on every connection.
Traces how the cipher_suites list and its exact order in a TLS ClientHello identify a client, why that order is fixed per build, and why reordering it to evade detection becomes its own signal.
How the order of TLS extensions became a fingerprint, why Chrome started shuffling that order in early 2023 with a Fisher-Yates permutation in BoringSSL, and how JA4 answered by sorting the list.
How detectors catch tools that forge a perfect browser ClientHello: the mismatch between the TLS layer and the HTTP/2 frames above it, library-specific residue, header order, and version drift.
How TLS session resumption becomes a fingerprint and a behavior signal: session tickets, TLS 1.3 PSK, 0-RTT early data, and the resumption patterns that reveal a client or break a spoof.
Traces how the ALPN protocol list in a TLS ClientHello, and its predecessor NPN, fingerprints a client, and why the offered protocols and their order must agree with the HTTP layer that follows or the whole session looks forged.
How ECH encrypts the inner ClientHello, including SNI, with an HPKE key fetched from DNS, what the outer ClientHello still leaks, and where deployment actually stands now that RFC 9849 has shipped.
A version-by-version catalog of how Chrome, Firefox, Safari, and Edge ClientHellos changed from 2015 to 2026: GREASE, extension permutation, post-quantum key shares, and what a current per-browser fingerprint looks like.
Where TLS fingerprints are actually computed in a server stack: the OpenSSL and BoringSSL callbacks that hand you the raw ClientHello, the nginx, HAProxy, and Envoy modules built on them, and the constraints that decide whether you get the bytes at all.
Traces how a default Python requests handshake gives itself away in the ClientHello: the OpenSSL cipher list, the extension set, the missing GREASE, and why curl-cffi and uTLS-style impersonation exist.
A reference for the HTTP/2 client fingerprint: the SETTINGS frame parameters, the WINDOW_UPDATE increment, the priority frames, the pseudo-header order, and the S|WU|P|PS string Akamai popularised in 2017.
Traces how the order of the :method :authority :scheme :path pseudo-headers fingerprints an HTTP/2 client, why each browser and library orders them differently, and why the signal is a clean browser-vs-client tell that survives header spoofing.
Traces HTTP/2 stream prioritization as a fingerprint: how browsers built dependency trees, why RFC 9113 deprecated the original scheme, what RFC 9218 replaced it with, and which part of the priority signal still labels a client.
How a QUIC client gives itself away before any application data flows: the unencrypted Initial packet, the transport parameters in the TLS-in-QUIC ClientHello, version negotiation, and why server-side detection here is still near-greenfield.
A packet-level tour of the QUIC Initial: long header, version, connection IDs, the salt-derived initial keys, datagram padding, packet coalescing, and what stays observable on the wire for fingerprinting.
How a forged TLS handshake plus a generic HTTP/2 library still contradicts itself at the frame level, and how anti-bot systems turn that cross-layer mismatch into a bot verdict.
Traces how HTTP/1.1 header order and field-name casing fingerprint a client, why every browser and library emits a fixed sequence, and how HTTP/2's mandatory lowercasing erased half the signal while keeping the rest.
Traces how the three Accept request headers, their exact default values, q-value syntax, and ordering form a per-browser signature, and how a missing or mismatched triad marks a request as a non-browser client.
Traces how HTTP/2's single multiplexed connection replaced HTTP/1.1's pool of short-lived sockets, and how that one architectural change turned per-connection setup frames into a stable, reusable client fingerprint.
Traces how the initial TTL, TCP window size, MSS, and the order of TCP options in a single SYN packet identify the sending operating system, and why that identity is set by the kernel rather than the browser.
How p0f reads a single SYN packet to name the operating system behind it, traced from Michal Zalewski's 2000 release through the v3 rewrite, the signature grammar, and why TTL, window size, and option order still leak OS identity in 2026.
Traces how the order of TCP options in a SYN packet, the window-scale shift count, the SACK-permitted flag, NOP padding, and the timestamp clock identify an operating system, and how per-connection randomization changed what the timestamp leaks.
How anti-bot systems catch a proxy by comparing the OS in the HTTP User-Agent against the OS inferred from the raw SYN packet, why the two disagree, and where the check breaks down.
How tunnels and VPNs shift MTU and MSS, why a non-standard MSS in a SYN packet betrays an encapsulated path, and how path-MTU discovery behavior turns a packet-size value into a signal.