How TCP/IP fingerprinting works: how TTL, window size, MSS, and TCP option order reveal your OS from the handshake — and how p0f and JA4T read them.
Before your browser sends a single byte of a TLS ClientHello, your operating system has already introduced itself. The TCP three-way handshake that opens every connection carries a handful of low-level parameters — the initial TTL, the advertised window size, the maximum segment size, and the order of TCP options — that your OS kernel fills in with its own defaults. None of those values is chosen by you or your application, and the combination is consistent enough to guess which operating system you are running. That is TCP/IP fingerprinting: passive OS detection from the raw packet headers, performed before any encryption is negotiated.
Key Takeaways
- The OS kernel writes the fingerprint, not the browser. The initial TTL, TCP window size, MSS, and TCP option ordering in the SYN packet are set by the operating system's network stack — so the fingerprint identifies the OS, not the app making the request.
- It happens before TLS. TCP/IP fingerprinting reads the SYN packet of the three-way handshake, one layer below the TLS handshake that JA3/JA4 fingerprint. The two stack on top of each other.
- It is passive. A server can fingerprint your OS just by accepting the connection — no probes, no JavaScript, nothing sent back to you. Tools like p0f pioneered this; JA4T is the modern, structured take.
- It reveals OS-vs-claim mismatches. A SYN packet whose defaults say "Linux" arriving with a
User-Agentthat claims "Windows Chrome" is a classic spoofing signal used in bot detection. - NAT, proxies, and VPNs blur it. Because the signals live in headers that gateways and tunnels rewrite, the fingerprint reflects the nearest stack that wrote the packet — which is exactly why it helps detect VPNs and proxies.
What Is TCP/IP Fingerprinting?
Every TCP connection opens with a three-way handshake: the client sends a SYN, the server replies SYN-ACK, and the client confirms with ACK. That first SYN packet is assembled by the operating system's TCP/IP stack, and several of its fields are not specified by the protocol standards — the standards define the meaning of each field but leave the default values to the implementation. Different kernels pick different defaults, and they rarely change between connections.
Because those defaults are baked into the OS network stack, the fingerprint describes the operating system and its version, not the browser or the user. Two different browsers on the same Windows machine produce the same TCP/IP fingerprint; the same browser on Windows and on Linux produces two different ones. This is the inverse of browser fingerprinting, which identifies the application and is blind to the kernel underneath.
The Signals in a SYN Packet
Four header values do most of the work, drawn from the IP layer (RFC 791) and the TCP layer (RFC 9293).
1. Initial TTL (Time To Live)
The IP header's TTL field is decremented by one at every router hop, so an observer never sees the original value directly — but operating systems start from a small set of well-known defaults: 64 (Linux, macOS, modern BSD), 128 (Windows), and 255 (some network appliances and older Unix). If a packet arrives with a TTL of 57, it almost certainly started at 64 and crossed 7 hops. Rounding the observed TTL up to the nearest common default recovers the origin OS family.
2. TCP Window Size
The TCP header advertises an initial receive window — how many bytes the sender is willing to accept before requiring an acknowledgement. The exact starting value is stack-specific: historically Windows, Linux, and macOS each shipped recognizably different initial window sizes, and the value is one of the strongest discriminators in the SYN. Combined with the window scale option (RFC 7323), it narrows the OS and sometimes the version.
3. Maximum Segment Size (MSS)
The MSS option (RFC 6691) declares the largest TCP payload the client will accept in one segment. Its value is derived from the link's MTU — typically 1460 bytes on standard Ethernet (1500 MTU minus 40 bytes of headers), but lower over PPPoE, tunnels, or VPNs. An unusual MSS is itself informative: a value of 1400 or below often betrays that the traffic is passing through a tunnel, which is a hint that a VPN is in use.
4. TCP Options and Their Order
The TCP options field can carry MSS, window scale, SACK-permitted, timestamps, and NOP padding — and crucially, the order in which a stack lists them is implementation-specific. Linux, Windows, and macOS each emit a characteristic option layout (for example MSS, SACK, Timestamp, NOP, Window-Scale versus MSS, NOP, Window-Scale, SACK, Timestamp). Like the extension order in a TLS ClientHello, the ordering leaks more identity than any single value.
| Signal | Set by | Typical Linux | Typical Windows |
|---|---|---|---|
| Initial TTL | IP stack | 64 | 128 |
| Window size | TCP stack | stack-specific | stack-specific |
| MSS | derived from MTU | 1460 | 1460 |
| Option order | TCP stack | distinct layout | distinct layout |
Passive Detection: p0f and JA4T
The defining property of TCP/IP fingerprinting is that it is passive. Unlike active OS-detection tools that send crafted probes and measure the responses, a passive fingerprinter only needs to observe traffic that the client sends anyway. Accepting an ordinary connection is enough to read the SYN.
p0f is the tool that popularized this approach — a passive fingerprinter that matches observed SYN packets against a database of known OS signatures, identifying the operating system, and sometimes the uptime or link type, without sending a single packet to the target. It established the signature format that most passive OS detection still follows.
JA4T is the TCP-layer member of the JA4+ suite from FoxIO, the same project behind the modern JA4 TLS fingerprint. JA4T composes the TCP window size, the TCP options in order, the MSS value, and the window scale value into a single structured, human-readable string — bringing the "sortable, readable fingerprint" philosophy of JA4 down to the transport layer. Because it sits below TLS, JA4T can flag a connection before the ClientHello is even parsed.
How It Stacks With TLS Fingerprinting
TCP/IP fingerprinting and TLS fingerprinting read adjacent layers of the very same handshake sequence, and they identify different things:
| Layer | Technique | Identifies |
|---|---|---|
| IP + TCP | p0f / JA4T | The operating system kernel |
| TLS | JA3 / JA4 | The TLS library (and thus the application) |
That difference is what makes the pair powerful. The TCP/IP fingerprint says which OS sent the packet; the TLS fingerprint says which client software opened the session. When the two disagree — a Windows-style SYN carrying a TLS handshake that matches macOS Safari — the connection is internally inconsistent, and inconsistency between observable layers is the core principle behind bot detection techniques. A scraper running on a Linux server that spoofs a Chrome-on-Windows User-Agent still emits a Linux SYN packet, and no header it sets can change that.
What It Reveals — and Its Limits
A TCP/IP fingerprint can reveal the OS family and version, hint at the link type (Ethernet, PPPoE, tunnel) from the MSS, and expose a mismatch between the claimed and actual platform. But it is a blunt instrument compared with higher-layer fingerprints, and several things blur it:
- NAT and load balancers can rewrite or normalize TCP options, so the fingerprint may reflect a gateway rather than the end host.
- VPNs and proxies terminate or re-encapsulate the connection; the server then fingerprints the VPN endpoint's OS, not yours. This is double-edged — it hides your real OS, but a residential-looking IP paired with a data-center OS fingerprint is itself a VPN/proxy signal.
- Low resolution. TCP/IP fingerprinting distinguishes OS families well but cannot identify a specific browser, device model, or user. It is a coarse first filter, not a unique identifier.
For these reasons it is almost always used as one input among many — combined with TLS and HTTP/2 fingerprints, IP reputation, and behavioral signals — rather than as a standalone verdict.
Frequently Asked Questions
Can I change my TCP/IP fingerprint?
Only by changing the OS network stack that produces it. Some privacy and anti-censorship tools offer "OS fingerprint spoofing" that rewrites TTL, window size, and option order to mimic another system, but this operates at the kernel/firewall level — browser settings, extensions, and User-Agent overrides cannot touch it, because the values are written before any application code runs.
Does a VPN hide my TCP/IP fingerprint?
Partly. A VPN tunnels your traffic, so the destination server sees the SYN packet produced by the VPN client's stack or its exit node, not your original one. Your real OS fingerprint is hidden, but the tunnel often leaves its own marks — a reduced MSS from encapsulation overhead, or an OS fingerprint that doesn't match the residential IP — that detection systems use to infer a VPN is present.
Is TCP/IP fingerprinting the same as Nmap OS detection?
No. Nmap performs active OS detection — it sends a series of crafted probe packets and analyzes the responses. TCP/IP fingerprinting as discussed here is passive: it only observes the packets a client sends during a normal connection, which makes it silent and undetectable from the client side.
Why does TTL alone not identify an OS?
Because TTL is decremented at each router hop, the value you observe is the starting default minus the hop count, and different defaults can overlap after enough hops. TTL narrows the OS family (64 vs. 128 vs. 255) but is only reliable when combined with window size, MSS, and option ordering — the full signature, not any single field.


