Server-Side Fingerprinting: Four Confessions Before the Page Loads

Old spymasters kept an apothegm: the finest informants never realize they're informing. Your browser fits that mold with uncanny fidelity. No spite drives the betrayal. Protocol does. Literally the network protocols your browser speaks aloud, before a single pixel renders.
Nine fingerprinting talks out of ten kick off with JavaScript. Canvas, WebGL, AudioContext, font enumeration. People bolt on extensions, spoof APIs, bask in green checkmarks from their antidetect tool. Exhale. Feel invincible. Jason Bourne breezing through Heathrow on a clean passport.
Bourne didn't know the airport had tagged him before he reached the gate. By his gait.
Four handshakes fire between pressing Enter and JavaScript seizing the wheel. A TCP SYN packet exposes your operating system. A TLS Client Hello surrenders your browser build. An HTTP/2 SETTINGS frame broadcasts your dialect. HTTP headers leak the order your browser arranges its own thoughts. One hundred milliseconds. You haven't blinked.
Browser extensions can't forge TCP packets. Kernel land. Can't snare a TLS Client Hello. Compiled network stack code. Won't touch HTTP/2 SETTINGS. Hardcoded deep in Chromium source. Manifest V3 doesn't reach this stratum. Nothing from the world of two-click installs does.
In brief: Within 100 ms the server pins your OS, browser, VPN, and route. No JavaScript. No cookies. Four handshakes your browser runs on autopilot. Extensions can't fake any of it because extensions live upstairs. Interrogation runs in the basement.
First Confession: TCP. Zero Milliseconds
First impressions are indelible. In TCP, yours is a SYN packet. One packet. Zero payload. Bare clockwork. Already a full confession.
Every operating system builds its TCP stack with a slightly different hand. Not what gets sent, but how the sending gets shaped. Two decades back, Microsoft's engineers chose one set of knobs for Windows networking. Linux engineers chose another. Decisions forged in offices that no longer stand, by coders who've long since moved on, remain tattooed into the kernels of billions of machines.
| OS | Window Size | TCP Options | MSS | Window Scale |
|---|---|---|---|---|
| Windows 10/11 | 65535 | 2-4-8-1-3 | 1460 | 8 |
| Linux 5.x+ | 29200 | 2-4-8-1-3 | 1460 | 7 |
| macOS | 65535 | 2-4-8-1-3 | 1460 | 6 |
| iOS | 65535 | 2-4-8-1-3-0 | 1460 | 6 |
| Android 13 | 65535 | 2-4-8-1-3 | 1460 | 9 |
iOS tacks on TCP Option 0, End of List, at the tail. Nobody else does. Why Apple made that call remains an enigma. For anyone watching, the meaning is pellucid: Hello, I'm an iPhone. Android cranks window scale to 9, beyond any desktop OS. Windows outright refuses to send TCP Timestamps, almost on principle. Each OS leaves an atavistic thumbprint, unbidden and undisableable, in that very first packet.
John Althouse at FoxIO codified all of this in 2024 with JA4T. A fingerprint like 29200_2-4-8-1-3_1424_7 decodes to: Linux, with MSS 1424 instead of the standard 1460. That 36-byte shortfall is tunnel overhead. Teaming up with GreyNoise, they ran JA4T against global traffic and surfaced hundreds of IPs sharing one fingerprint. All Tencent. Hundreds of scanning rigs, outed by a single field in a single packet. One.
MSS is an X-ray of your route. 1460 means bare Ethernet, nothing between you and the server. 1380 means VPN or tunnel; encryption ate 80 bytes. WireGuard has its overhead, OpenVPN its own, IPsec a third. Althouse showed JA4T can sometimes tell T-Mobile from Verizon off a single SYN packet. Your mobile carrier, from one packet.
WHOX checks this in signals A06 and C09. That work wraps up before your browser sends its first byte of content. You haven't knocked. You've already been frisked.
A Quarter Century of Eavesdropping
None of this started in 2024. On June 10, 2000, twenty-five years ago, when the internet still reeked of dial-up and guileless optimism, Michal Zalewski posted p0f to Bugtraq. Passive OS fingerprinting. Without sending a single packet. Just listen to how other machines greet the wire and read who's speaking. TTL, window size, MSS, the Don't Fragment bit, TCP options order. Every OS had its own telltale quirks.
Unlike nmap, which kicks doors down and trips every plangent alarm in sight, p0f sat motionless. A wraith. You can't spot a watcher who sends nothing. A security camera with no blinking red light.
OpenBSD wove p0f logic into its PF firewall in 2003. Admins wrote rules: let Linux through, turn Windows away. At the network stack level. That core idea hasn't budged in two decades. What shifted is the sinew of the tooling, by orders of magnitude.
eBPF: Fingerprinting at Kernel Speed
In 2025, Alberto Ventafridda shipped a Go webserver using eBPF to snag TCP SYN right at the network interface, before the packet reaches the kernel's TCP stack. Fingerprint stored in an eBPF hashmap in kernel-space. Zero copies. Zero threads. Zero added latency.
By his own telling: the LLM scraping arms race built demand for a tool that reads your fingerprint before you've even knocked. Generative AI craves data. Data holders need bulwarks against those who pilfer it. One side effect of that war: a tool that pins you from your first packet. Cheers, ChatGPT.
Models That Don't Travel
Ifip Networking 2025: Random Forest on TCP/IP headers hits a brilliant F1-score. A model trained on one ISP's traffic, however, crumbles on another's. Every watcher sees its own rendition of your fingerprints. Cold comfort: you're not tracked by one panoptic eye but by a welter of half-blind ones. Fragmented surveillance is still surveillance.
Hidden Layer: Heartbeat, Not Signature
TCP congestion control algorithms can be read passively. TU Munich proved this in 2022. Linux defaults to Cubic. Google servers run BBR. Windows 11 24H2 bolted on BBRv2. You can tell them apart by watching congestion window patterns. Not a signature. A cardiac rhythm. Each OS has its own heartbeat, and a stethoscope sits on the far end of the wire. No extension steers how your kernel handles congestion. One more lambent vector nobody thinks about. Yet.
Second Confession: TLS Client Hello. 20-50 ms
Loose lips sink ships. British wartime counsel that hasn't aged a day. TLS Client Hello has been sinking ships since 1999.
TCP link is up. Now the browser sets up encryption by firing a TLS Client Hello. In the clear. You've got to agree on how to encrypt before you can encrypt. Classic catch-22. And the catch already snared you one handshake earlier.
Packed inside that Client Hello is a voluble dossier of your software stack, broadcast unencrypted to every device between you and the server. Supported TLS versions, cipher suites, extensions, elliptic curves, Alpn picks. Chrome looks nothing like Firefox. A Python script looks like neither. Different builds of the same browser yield distinct fingerprints. You might as well walk into a job interview and blurt your name, age, schooling, and blood type in the opening breath. Nobody asked.
King Is Dead. Long Live the King.
Salesforce released JA3 in 2017: a hash of the Client Hello drawn from cipher suite and extension order. Elegant. Worked beautifully. Chrome 108 then scrambled TLS extension order. Same browser, different JA3 each time. End of an epoch.
JA4 answered with a deceptively simple fix: sort extensions before hashing. Shuffle all you like. After sorting, order locks. Peakhour reports 99% identification accuracy. Chrome can shuffle the deck till Judgement Day. Dealer still knows the hand.
Anatomy of a JA4: t13d1516h2_8daaf6152771_02713d6af862. First segment: protocol, version, SNI, cipher count, extension count, Alpn. Second: hash of sorted ciphers. Third: hash of sorted extensions. One string. Three tiers of ID. Your full name spelled out in TLS.
Scale That Numbs the Hands
Cloudflare crunches 15 million unique JA4 fingerprints per day. Sees 500 million user agents. Billions of IPs. In October 2025, they rolled out JA4 Signals: metrics drawn from all global traffic over the past hour. For each fingerprint: which user agents typically ride alongside it, from which countries, at what hours, at what cadence. Not your traffic. Planetary traffic, indexed by JA4.
Adopters: Cloudflare, AWS, Google Cloud, Azure, Akamai, Fastly, Vercel, Palo Alto, CrowdStrike, Wireshark, Zeek, Suricata, HAProxy, Nginx, F5. Read that list twice. Hardly a moribund backwater. Just the plumbing of the internet. Cloudflare's Client Hello parser: rewritten in Rust, one crate, zero heap allocations, Simd hex encoding, two million elements per second. Running in production on 20% of global web traffic.
When people say nobody's watching, that list is watching. Around the clock. On every handshake.
ECH Paradox, or How a Shield Became a Beacon
Encrypted Client Hello, RFC greenlit October 2025, encrypts the SNI: hides which site you're visiting. Sounds like a win. Cisco EVE in December 2025, however, showed session identification with 100% confidence from the Outer Client Hello and TLS fingerprint even with ECH switched on.
ECH encrypts the address on the envelope. Handwriting, paper, stamp, return address: all still bare. You encrypted where. You never encrypted who.
Worse yet. ECH fattens the fingerprinting surface. An Outer Client Hello bearing its Grease ECH extension pattern becomes a fresh identification vector. Browsers running ECH look different from browsers without it. Per Mdpi 2025, across 106 million QUIC connections, real ECH use hovers around 2%. Two percent. By flipping ECH on for privacy, you've joined a two-percent cohort that's simpler to track than the ninety-eight percent who didn't.
Wanted to vanish. Donned a neon gilet instead. Not a bug. Arithmetic.
Third Confession: HTTP/2 SETTINGS. 50-80 ms
TLS handshake done. Channel locked. Private at last? Not quite.
Your browser fires an HTTP/2 SETTINGS frame, the very first frame on any HTTP/2 link. It lays bare the parameters your browser deems best. Ory Segal at Akamai spelled this out in a Black Hat EU 2017 whitepaper: SETTINGS works as a liminal tell. Everyone speaks HTTP/2, but each speaker carries a telltale lilt.
| Parameter | Chrome | Firefox | Safari | curl |
|---|---|---|---|---|
| HEADER_TABLE_SIZE | 65536 | 65536 | 4096 | 4096 |
| ENABLE_PUSH | 0 | 0 | 1 | 0 |
| MAX_CONCURRENT_STREAMS | 1000 | 100 | 100 | 100 |
| INITIAL_WINDOW_SIZE | 6291456 | 131072 | 2097152 | 1073741824 |
| MAX_HEADER_LIST_SIZE | 262144 | 65536 | > | > |
curl sets INITIAL_WINDOW_SIZE to a full gigabyte. One billion seventy-three million bytes. No living browser does this. Spot that number and you know: the far side isn't a person. Some script is straining to pass for human and botching it on parameter one. Walking into a costume ball wearing a trench coat stenciled Definitely A Human.
Beyond SETTINGS, pseudo-header order differs. Chrome sends :method, :authority, :scheme, :path. Firefox and Safari shuffle them differently. You can't tune that order. Compiled into the source. Like syllable stress in a regional accent, it tells the listener where you hail from.
Harder to Fake Than You'd Guess
Swapping a TLS fingerprint means recompiling OpenSSL or trading out the library. HTTP/2 SETTINGS are baked into the browser's network stack. Shifting them demands a fork. Not an extension. Not a toggle. A fork of Chromium with rewritten network code.
Exactly how curl-impersonate came to be: a complete fork of curl with patched BoringSSL and hand-copied HTTP/2 parameters from Chrome, Firefox, and Safari. Builders crawled through each browser's source, yanked out the values, rebuilt them painstakingly. Faking one network fingerprint meant forking one of the internet's bedrock tools. Like forging someone's handwriting by learning calligraphy from scratch. Feasible. But few will bother.
WHOX catches the mismatch in signal A18. Browser claims I'm Chrome. SETTINGS say curl. Like someone claiming London roots who says bath to rhyme with math.
Hidden Layer: Priority as Generational Fingerprint
HTTP/2 Priority frames reveal which resources the browser grabs first and what stream dependencies it sets. Chrome's priority model shifted hard between HTTP/2 and HTTP/3. A temporal fingerprint emerges: Priority pins not just the browser but a narrow version window. Update history woven into network behaviour, exiguous yet ineluctable. Your browser wears its birthday on its forehead.
Fourth Confession: HTTP Headers. 80-100 ms
At last the browser sends its GET request with headers. After three confessions, surely nothing's left to wring out?
Everything.
HTTP header order is unique per browser. Chrome puts Host first, then Connection, then Accept. Firefox arranges them another way. No spec governs order. Each engine picks its own sequence. Each picks differently. Like how people shelve books: by author, by spine colour, by purchase date. A habit you can't counterfeit unless you know it's there.
JA4H hashes that order into a fingerprint. You can't shift header order from an extension. Headers get shaped by the network stack before any extension receives a call.
Accept-Language as tripwire. Chrome sets q=0.9 for secondary languages. Some antidetects use q=0.8. One tenth. One decimal place. Instant detection. In tradecraft they'd call this a canary: a minuscule datum that burns the whole cover.
Client Hints: Wolf in Sheep's Fleece
User-Agent Client Hints were pitched as a privacy-first stand-in for the User-Agent string. Sites must ask for data outright, promising tighter control and fewer leaks. Laudable aims pave treacherous roads.
KU Leuven, Wpes 2023: Client Hints show up on 59.2% of the top 100K sites. 93.8% of fetches come from tracking and ad scripts. 91.6% of that data ships to outside servers. A tool built for privacy, wielded for surveillance nine times out of ten. Orwell wouldn't have flinched.
WHOX checks JA4 against User-Agent in signal C12. TLS Client Hello says Chrome 120. User-Agent agrees. JA4 hash, however, doesn't match Chrome 120's benchmark. Someone forged the business card but forgot to fix the accent. How antidetects, bots, and scrapers get caught.
Hidden Layer: Hpack, Memory That Leaks
HTTP/2 squeezes headers through Hpack. A dynamic table fills with data from earlier requests. First request: everything in full. Second: partly indexed. Third: more indexes. By the tenth, two different users' tables have fully diverged because they clicked different links. Servers see which indexes you use and rebuild your browsing trail, a vermicular trail of every page you touched.
No papers exist on this in the fingerprinting world. Yet the mechanism runs on every HTTP/2 link on Earth right now. A built-in protocol feature nobody has weaponized. Operative word: yet. We're all grown-ups here.
Off-Camera: QUIC and HTTP/3
38.1% of sites per W3Techs as of February 2026 run on QUIC, and by traffic volume it's far more because Google, YouTube, and Cloudflare dominate. New protocol, new fingerprints.
A QUIC Initial packet holds a TLS 1.3 Client Hello. Encrypted, supposedly. In practice the encryption key derives from the public Connection ID. Anyone who's read the spec can read your Client Hello. A padlock with the key taped to its own face.
VisQUIC, arXiv 2024 to 2025: 100,000+ traces from 44,000 sites. Accuracy in tagging HTTP/3 responses from encrypted QUIC: 97%. Classifiers trained on TCP, meanwhile, break on 96% of QUIC visits. Two worlds. Two fingerprint sets. Double the toil for anyone trying to vanish.
0-RTT: Cookies Baked Into the Transport
QUIC supports 0-RTT resumption. Your browser sends data in its very first packet using a session ticket from a prior link. Fast. Handy. And in practice a tracking token welded into the transport layer that users don't know about and can't wipe through Clear Browsing Data.
Session tickets chain the current link to the old one. Servers know you're the same client. Not from cookies. Not from IP. From a cryptographic token in the first packet. Cleared your cookies? Gone incognito? Switched profiles? That session ticket lives one floor below all of it, in a refractory stratum where Clear buttons don't reach.
A technology built for speed became a surveillance instrument. Faster links, thinner privacy. An engineering tradeoff they forgot to mention. Forgot, or chose not to.
Why Antidetects Are Powerless
Antidetect browsers live in JavaScript's kingdom. Canvas, WebGL, navigator.userAgent, fonts: all spoofable. DOM and APIs. Cozy. Familiar. Tractable.
Network stacks occupy a different realm entirely. A basement JavaScript never enters.
An antidetect's JA4 never matches the JA4 of the browser it claims to be. Most common tells, plain to the naked eye:
- Chrome UA and curl JA4. Someone took curl, set User-Agent to Chrome, and called it a day. Like wearing a pilot's cap into a cockpit. Cap's right. Everything else is wrong.
- Chrome UA and Firefox HTTP/2 SETTINGS. An antidetect built on Firefox, masquerading as Chrome. Wrong accent.
- Windows UA and macOS TTL. Spoofed the OS in headers, forgot the TTL. New passport, old tattoo.
curl-impersonate exists precisely because you can't tweak curl into a Chrome simulacrum. A full fork with patched TLS and HTTP/2 is the bare minimum. Even then the match isn't flawless.
dMAP, NDSS 2025: obfuscated proxy traffic spotted with 95% accuracy through cross-layer RTT analysis. Servers measure the gap between TCP ACK and HTTP response. A proxy adds a hop. That hop adds milliseconds which are physically impossible to hide. You can fake TLS. You can fake User-Agent. You can't fake the speed of light.
WHOX catches this across four signals: A18, C12, C09, A06. Four checks, four layers, none needing JavaScript.
Full Picture: One Hundred Milliseconds, Five Floors
| Time | Layer | What the Server Learned | How |
|---|---|---|---|
| 0-1 ms | TCP SYN | OS, VPN, mobile carrier | JA4T, p0f, eBPF |
| 20-50 ms | TLS Client Hello | Browser, version, TLS stack | JA4 |
| 50-80 ms | HTTP/2 SETTINGS | HTTP/2 dialect, automation | Akamai H2 FP |
| 80-100 ms | HTTP Headers | Header order, language, Client Hints | JA4H |
| 100+ ms | QUIC | Transport params, 0-RTT | JA4Q, VisQUIC |
By the time JavaScript takes the wheel, five interrogations have run. Your browser extension sat out every one. Didn't even know they were happening.
FAQ
Does a VPN shield you from TCP fingerprinting? A VPN swaps your IP. Your OS shapes the TCP fingerprint before the packet enters the tunnel. Window Size, TCP Options, Window Scale stay yours. Only thing the VPN shifts is MSS, because encryption overhead shrinks it, and that shrinkage is itself a signal. A VPN doesn't block TCP fingerprinting. Feeds new data into it. Came for the cure, left with a fresh diagnosis.
Does Tor spoof TCP/TLS fingerprints? Tor Browser locks its TLS Client Hello to one shape for all users. Everyone looks alike. TCP fingerprints travel through three nodes and exit with the exit node's numbers, not yours. Problem solved. At the cost of speed, usability, and a patience that a sliver of a percent of users possess.
How does JA4 handle extension randomization? Sorts before hashing. Shuffle all you like. After sorting, order locks. 99% accuracy per Peakhour. Chrome can shuffle the deck forever. Dealer still knows the hand.
Can antidetect browsers spoof HTTP/2 SETTINGS? In theory, yes, if you fork Chromium and rewrite the network code. In practice, no mass-market antidetect has pulled it off. curl-impersonate did, and the upshot was a complete curl fork that must be rebuilt for each target browser. Not install an extension. More like rebuild the groundwork.
What are Cloudflare's JA4 Signals? A roll-up of all global traffic over the past hour. For each JA4: which UAs typically travel with it, from which countries, at what rate. Your JA4 matches a fingerprint usually tied to Python bots? Cloudflare knows. Not from your behaviour, but from the statistics of seven billion people. A personal dossier built from planetary averages.
Read Next
- The First 100 Milliseconds: What Servers Know Before the Page Loads
- How Antidetect Browsers Get Caught
- WebGPU, QUIC, eBPF: The Future of Fingerprinting
- What Actually Works for Privacy
WHOX - We tell real signals from manufactured ones. Check your fingerprint at whox.com and find out what signals leak from your browser and how unique you are.
Related Articles
"Do Not Blow Your Cover": Deconstructing AI Authorship Concealment
Anthropic's Claude Code leaked. We reverse-engineered the concealment architecture: seven interlocking systems from Undercover Mode to anti-distillation, tested against 28 public PRs from Anthropic employees, and mapped every layer the system covers and every one it doesn't.
ResearchThe Randomization Lie: Every Canvas Defense Has Been Defeated
WWW'25 proved it: all 18 extensions and 5 browsers defeated. Canvas noise isn't protection. It's a marker. Zero working defenses.
ResearchThe Entropy Map: Which Browser Signals Give You Away
Skin ridges make every human singular. Browsers carry their own version of that mark, and they're far less coy about handing it over. You never held out your palm. Your browser did it for you, silently, on every HTTP request, to every site, without a whisper of consent.