Skip to content

TCP/IP Fingerprinting: Why Every Stat Proxy Now Looks Like Windows

By Nicholas St. Germain. Published

Your scraper sends a perfectly normal request. The User-Agent says Chrome on Windows 11. The TLS handshake matches real Chrome. The cookies are warm and the IP has a clean history on a residential ISP network. The site challenges you anyway.

A common reason is something most people never look at: the very first packet of the connection. Before a single byte of HTTP is exchanged, the TCP handshake has already told the site which operating system opened it. If that answer is "Linux" and your browser claims "Windows", you have handed the site a contradiction, and anti-bot systems are built to notice contradictions.

As of October 2026, every connection through a Stat Proxies IP opens like a Windows machine by default, on HTTP, HTTPS and SOCKS5, with nothing to configure. If your client is not Windows, you can pick the profile per connection with a flag in the proxy username: macOS, iOS, Android or Linux. This post explains what TCP/IP fingerprinting is, why proxies get it wrong, what the profiles do, and how to choose one.

What TCP/IP fingerprinting is

Every TCP connection starts with a three-way handshake. Your machine sends a SYN packet, the server answers with SYN-ACK, and your machine replies with ACK. The SYN is the interesting one. To build it, your operating system has to fill in a set of fields: how many hops the packet may travel, how much data it can receive before an acknowledgement, which TCP options it supports, and in which order it lists them.

None of these values are dictated by the TCP standard. Each OS vendor picked its own defaults years ago and mostly kept them. Windows, macOS and Linux each write the SYN a little differently, the way people with the same handwriting lesson still end up with different signatures.

Reading those differences off live traffic is called passive OS fingerprinting. "Passive" means the observer sends nothing; it reads the packets you were going to send anyway. The best-known tool is p0f, first released by Michal Zalewski around 2000, which matches incoming SYN packets against a database of signatures and prints a guess like "Windows 7 or 8" or "Linux 3.11 and newer". The same idea now sits inside commercial bot detection, CDN edge logic and fraud scoring, usually as one input among many.

A site can do this at almost no cost. The SYN arrives before TLS, before HTTP, before any JavaScript runs. It cannot be blocked by a browser extension, and the client never sees that it was read.

The fields that give an OS away

These are the values a fingerprint is built from. Most of them sit in the IP and TCP headers of the SYN; a couple show up over the life of the connection.

Field What it is Why it differs by OS
Initial TTL A hop counter. Each router decrements it by one. Windows starts at 128. Linux, Android, macOS and iOS start at 64.
TCP option order The sequence of options (MSS, window scale, SACK, timestamps, padding) in the SYN. Each stack writes them in its own fixed order. This is the most stable signal.
Initial window size How many bytes the client can receive before it must acknowledge. Different defaults per OS and kernel version.
Window scale A multiplier for the window size. Windows commonly uses 8, Linux 7, macOS 6.
TCP timestamps An option carrying a clock value. Windows leaves it off by default. Linux, Android, macOS and iOS send it.
IP ID and Don't Fragment The packet identifier and a flag saying "do not split this packet". Stacks differ in how they number packets. All modern ones set DF on the SYN.
ECN on the SYN Whether the client offers Explicit Congestion Notification. Apple platforms commonly offer it; Windows and Linux usually do not by default.
Source port range The range the OS picks local ports from. Windows and Apple use 49152 to 65535 (the IANA dynamic range). Linux uses 32768 to 60999.

A few of these deserve a closer look.

TTL is read with rounding. A site never sees your initial TTL directly. It sees what is left after the routers in between have each taken one off. A packet arriving with TTL 116 almost certainly started at 128 and crossed 12 hops; one arriving at 52 started at 64. Because real-world paths are rarely longer than 30 hops, the observer rounds up to the nearest common starting value (64, 128 or 255) and gets the OS family. This is why TTL alone splits the world cleanly into "Windows" and "everything else".

Option order is the strongest clue. Values like window size can drift between OS versions and get tuned by admins. The order in which a stack writes its options almost never changes, and nobody tunes it. A typical Windows 10 or 11 SYN lists MSS, NOP, window scale, NOP, NOP, SACK permitted. A typical Linux SYN lists MSS, SACK permitted, timestamp, NOP, window scale. You can tell them apart at a glance even if every number matched.

Timestamps are a tell in both directions. Windows does not send the TCP timestamp option on a normal outbound connection. If a "Windows" client sends one, something is off. If a "Mac" client does not, something is off the other way. When timestamps are present, the clock value itself carries information too: a counter that has been ticking since the machine booted can hint at uptime and can be used to link connections that came from the same host.

Here is roughly what the SYN looks like from each family, written in plain terms:

Windows 10/11     TTL 128 | window 64240 | scale 8 | MSS,NOP,WS,NOP,NOP,SACK      | no timestamps
macOS / iOS       TTL 64  | window 65535 | scale 6 | MSS,NOP,WS,NOP,NOP,TS,SACK,EOL | timestamps, ECN offered
Linux / Android   TTL 64  | window 64240 | scale 7 | MSS,SACK,TS,NOP,WS            | timestamps

Exact numbers vary by version, patch level and network (MSS, for example, depends on the path's MTU), so detection systems match patterns rather than single values. The pattern is what matters.

Why proxies break the fingerprint

Here is the part that catches people. A proxy does not forward your SYN packet to the destination. It cannot.

When you connect through an HTTP proxy, a CONNECT tunnel or SOCKS5, your machine opens a TCP connection to the proxy. The proxy then opens a second, separate TCP connection from its own network stack to the site. Your SYN ends at the proxy. The SYN the site receives was written by the proxy's operating system, with the proxy's TTL, the proxy's option order and the proxy's timestamps.

Your laptop (Windows)              Proxy server                  Target site
       |                                 |                             |
       |-- SYN (Windows fingerprint) --> |                             |
       |                                 |-- SYN (proxy's own OS) ---> |
       |                                 |                             |
       |   TLS + HTTP pass through the tunnel, but the site's view of  |
       |   the TCP layer is whatever the proxy's stack wrote.          |

So the TCP fingerprint a site sees has nothing to do with your device. It describes the proxy. And proxy servers, like nearly every server on the internet, almost always run Linux.

That produces the classic mismatch:

  • HTTP layer: User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/...
  • TLS layer: a real Chrome handshake, if you use a real browser
  • TCP layer: TTL 64, Linux option order, timestamps on

Real Windows Chrome cannot produce a Linux SYN. A site that compares the two layers learns, with high confidence, that the request passed through some intermediary. It does not learn that you are a bot, but "came through a Linux box while claiming to be a Windows desktop" is exactly the profile of most automated traffic, so it is a cheap and effective signal to weight.

Residential proxy networks built on other people's devices have the opposite problem: the fingerprint is whatever the exit device runs, which may be a phone, a smart TV or a router, and has no relation to the browser you are driving. Either way, the TCP layer and the browser disagree.

Why Windows is the right default

When we decided what every connection should look like out of the box, the answer was Windows, for three reasons.

First, it matches what most browsers in our customers' stacks say. A large share of browser automation and AI agent traffic presents a Windows Chrome User-Agent, because Chrome on Windows is the most common desktop combination on the web, and stealth setups pick it for exactly that reason. A Windows TCP fingerprint makes the network layer agree with the browser layer for the largest group of customers without anyone changing a line of code.

Second, it matches the network. Our IPs are static US addresses on residential ISP networks. The most ordinary thing a home or small-office connection carries is a Windows PC opening HTTPS connections. A Windows SYN from a residential IP is the least surprising combination there is.

Third, a Linux fingerprint is the signature of server traffic. Defaulting to it would mean every customer starts out looking like a data centre unless they knew to change it.

So as of October 2026, every connection through a Stat Proxies IP presents a Windows TCP/IP profile by default: Windows TTL, Windows option order, Windows window size and scale, and no timestamps. It applies on HTTP, HTTPS (CONNECT) and SOCKS5, on every IP, for existing orders as well as new ones. There is nothing to turn on.

Picking a different OS per connection

Windows is the right answer for most traffic, not all of it. If you drive Safari on macOS, emulate an iPhone, or run a stock headless Chrome that reports Linux, you want the TCP layer to say the same thing. For that, add an OS flag to the proxy username:

Username suffix Profile presented to the site
none, or -os-windows Windows (default)
-os-macos macOS
-os-ios iOS
-os-android Android
-os-linux Linux

The password, IP and port stay the same. Only the username changes. If your username is sub_example123, connect as sub_example123-os-macos to get the macOS profile.

The flag is read per connection, not per IP. One IP can carry a Windows connection and a macOS connection at the same moment, which is also what a normal household looks like from the outside: a laptop, a phone and a tablet sharing one public address.

With curl:

# Default: Windows profile
curl -x http://sub_example123:YOUR_PASSWORD@YOUR_PROXY_IP:YOUR_PORT https://example.com

# macOS profile on the same IP
curl -x http://sub_example123-os-macos:YOUR_PASSWORD@YOUR_PROXY_IP:YOUR_PORT https://example.com

# Android profile over SOCKS5
curl -x socks5h://sub_example123-os-android:YOUR_PASSWORD@YOUR_PROXY_IP:1080 https://example.com

With Playwright, keep the username and password in separate fields as usual and put the flag on the username:

import os
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.webkit.launch(
        proxy={
            "server": os.environ["STAT_PROXY_SERVER"],  # http://<proxy-ip>:<port>
            "username": os.environ["STAT_PROXY_USERNAME"] + "-os-macos",
            "password": os.environ["STAT_PROXY_PASSWORD"],
        }
    )
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.title())
    browser.close()

If you pull proxy lists from our management API, pass os and the list comes back with the flag already applied:

curl "https://dashboard.statproxies.com/api/v2/order/fetch/<order-id>?os=macos" \
  -H "Authorization: Bearer $STAT_API_KEY"

Every entry in that response uses <username>-os-macos, in whatever format you asked for, and it combines with protocol=socks5.

How to choose the right profile

The rule is simple: make the TCP layer agree with the OS your client claims. For a browser, that is the OS in its User-Agent and in navigator.platform and the Client Hints headers. Some common setups:

  • Chrome or Edge with a Windows User-Agent (most stealth and agent setups): leave it on the default.
  • Stock headless Chromium on a Linux server, User-Agent untouched: it reports X11; Linux x86_64. Use -os-linux, or better, run a Windows profile in the browser and keep the default.
  • WebKit or Safari profiles: -os-macos.
  • Mobile emulation: -os-ios for an iPhone User-Agent, -os-android for an Android one. Make sure the rest of the emulation (viewport, touch support, Client Hints) matches too.
  • Plain HTTP clients (requests, httpx, Go net/http): these do not claim an OS in their User-Agent, so the TCP profile matters less than the fact that their TLS fingerprint gives them away. Default is fine.

Two mistakes to avoid. Do not randomize the OS per request on the same session: a browser does not switch from Windows to macOS between page loads, and a session whose TCP layer flips around is less believable than one that is consistently wrong. And do not pick a profile because it seems "stealthier". There is no stealthy OS. There is only the OS that matches the rest of your fingerprint.

How to check what a site sees

You do not need to take this on trust. A few public pages read your connection's TCP fingerprint the same way a site would:

  • BrowserLeaks TCP/IP fingerprint shows the p0f-style OS guess for your connection next to the OS your browser reports, and flags a mismatch.
  • tls.peet.ws shows TLS and HTTP/2 fingerprints, which are the next layers to line up once TCP is right.

Load them once directly from your machine, once through a Stat IP with no flag, and once with a flag that matches your browser. The direct load shows your own OS. The default proxied load should read as Windows. The flagged load should read as the OS you asked for. If a check page reports a mismatch on the proxied load, the first thing to compare is the User-Agent your client sends against the profile you picked.

What this fixes and what it does not

A matching TCP fingerprint removes one contradiction. It does not make traffic invisible, and it is worth being plain about the limits.

It fixes:

  • The Linux-proxy-behind-a-Windows-browser mismatch, on every Stat connection, by default.
  • Mismatches for non-Windows clients, with one flag in the username.
  • Sites that weight the TCP layer heavily. Some sites lean on TCP fingerprinting much more than others, and those are where you will notice it most.

It does not fix:

  • Your TLS fingerprint. If your HTTP client's handshake looks like Python or Go, that is visible no matter what the TCP layer says. Use a real browser or a client that impersonates one.
  • Your HTTP/2 and header fingerprint. Header order, pseudo-header order and SETTINGS frames are their own signals.
  • JavaScript fingerprinting. Canvas, WebGL, fonts, screen size, timezone and the automation flags of headless browsers are read in the page, far above the network.
  • Behaviour. Request rate, navigation patterns and session length still decide most blocks. A perfect fingerprint sending 50 requests a second is still 50 requests a second.
  • Reputation. The site still decides whether it trusts the IP. A static IP with a clean history helps; a matching fingerprint does not repair a burned one.

The goal across every layer is the same: consistency. Detection works by finding the places where a client's story does not add up. TCP was one of the easiest places to find one through a proxy, and now it is one fewer.

FAQ

What is TCP/IP fingerprinting?

It is identifying a device's operating system from how its network stack builds packets, mainly the first SYN packet of a TCP connection. Values like the initial TTL, window size, TCP option order and whether timestamps are sent differ between Windows, macOS, Linux and mobile systems. A site can read them passively, before TLS or HTTP, without the client knowing.

Why does my proxy show up as Linux on fingerprint checkers?

Because the destination sees the proxy's TCP connection, not yours. A proxy opens its own connection to the site, built by its own operating system, and most proxy servers run Linux. Stat Proxies connections present a Windows profile by default, and you can choose macOS, iOS, Android or Linux per connection with a username flag.

How do I change the OS my Stat proxy connection looks like?

Append a flag to your proxy username: -os-macos, -os-ios, -os-android or -os-linux. Windows is the default, so no flag (or -os-windows) gives you Windows. The password, IP and port stay the same, and it works on HTTP, HTTPS and SOCKS5. The management API can also return proxy lists with the flag applied.

Which OS profile should I use?

The one your client claims to be. For a browser, match the OS in its User-Agent: Windows Chrome keeps the default, Safari or WebKit uses macOS, and mobile emulation uses iOS or Android. Keep the same profile for the whole session rather than switching between requests.

Does a matching TCP fingerprint stop all blocks?

No. It removes one signal that commonly gives proxied traffic away. Sites also check TLS and HTTP/2 fingerprints, run JavaScript fingerprinting, score behaviour and track IP reputation. A matching TCP layer helps most on sites that weight it heavily and helps everywhere by removing a contradiction, but the destination still decides.

Does this cost extra?

No. The Windows default and the OS flags are included on every Stat Proxies IP, for existing and new orders. Static ISP proxies are $2.50 per IP per month with unlimited bandwidth.