Skip to content

How to Check Your Proxy's TCP/IP Fingerprint (curl, Python, p0f, tcpdump)

By Nicholas St. Germain. Published

The quick answer

Send one request through your proxy to https://tls.peet.ws/api/all and read the tcpip.tcp_syn block in the JSON it returns. That block is the first packet of the connection as the server saw it, so it shows the TCP/IP fingerprint of your proxy, not your computer.

curl -s -x http://user:pass@proxy.statproxies.com:8080 \
  https://tls.peet.ws/api/all | jq .tcpip.tcp_syn

Replace the host, port, and login with the ones from your proxy dashboard. The rest of this guide covers what the fields mean, how to do the same check from Python, and how to check it on a server you control with p0f or tcpdump.

Method 1: tls.peet.ws

tls.peet.ws is a free fingerprint echo service. It captures the SYN packet of each connection and returns it as JSON next to your TLS and HTTP/2 fingerprints. Through a Stat Proxies IP, the TCP part looks like this (your TTL and port will differ):

{
  "ttl": 116,
  "df": true,
  "ecn": false,
  "window": 65535,
  "window_scale": 8,
  "sack_permitted": true,
  "timestamps": false,
  "option_order": "mss,nop,wscale,nop,nop,sackOK",
  "src_port": 51873
}

The same response also has a p0f field with the packet written as a p0f signature, which is handy for pasting into a ticket or a diff.

Run it twice, once through the proxy and once without, and compare. The direct run shows your own machine. The proxied run shows what every website sees from your proxy.

Method 2: the same check from Python

If your scraper is in Python, run the check with the same HTTP client and proxy settings you use in production:

import requests

proxy = "http://user:pass@proxy.statproxies.com:8080"
r = requests.get(
    "https://tls.peet.ws/api/all",
    proxies={"http": proxy, "https": proxy},
    timeout=20,
)
syn = r.json()["tcpip"]["tcp_syn"]
print(syn["ttl"], syn["window"], syn["window_scale"], syn["timestamps"], syn["option_order"])

The TCP values will match the curl run, because the proxy writes them. The TLS block in the same response will differ between curl and requests. That part comes from your client, and it is worth checking too.

Reading the result

Compare the fields to this table. The TTL a server sees is the sent value minus one per hop, so expect it a little under the sent value.

Field Windows 11 macOS / iOS Linux (Ubuntu)
TTL as received about 110-125 about 45-60 about 45-60
window 65535 65535 64240
window_scale 8 6 7
timestamps false true true
option_order mss,nop,wscale,nop,nop,sackOK mss,nop,wscale,nop,nop,timestamp,sackOK,eol mss,sackOK,timestamp,nop,wscale
src_port 49152 or higher 49152 or higher 32768-60999

Three quick tells for a Linux proxy:

  1. TTL below 64
  2. timestamps is true
  3. sackOK comes second in the option order

If your proxied run shows all three while your user agent says Windows, websites see the same mismatch.

Method 3: p0f on your own server

An echo service is convenient, but you may want to see the packet on a machine you control. p0f is a passive OS fingerprinting tool that has been around since 2000 and ships in most Linux package managers.

On any Linux server with a public IP:

sudo apt install p0f
sudo p0f -i eth0 'tcp dst port 443'

Then, from your own machine, send a request through the proxy to that server:

curl -s -k -x http://user:pass@proxy.statproxies.com:8080 https://YOUR_SERVER_IP/

p0f prints one block per SYN it sees. Look for the os line and the raw_sig line. Through a Stat Proxies IP, os reads as a Windows kernel and raw_sig starts with the TTL, has a window of 65535 with scale 8, and lists mss,nop,ws,nop,nop,sok. Nothing needs to listen on port 443 for this to work, because p0f reads the SYN before the server answers.

Method 4: tcpdump

If you want the packet itself, tcpdump on the same server shows it:

sudo tcpdump -ni eth0 -v 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0 and dst port 443'

Each line shows the TTL, the window, and the options in order. A Windows fingerprint looks like this:

IP (tos 0x0, ttl 116, id 23104, offset 0, flags [DF], proto TCP (6), length 52)
    203.0.113.10.51873 > 198.51.100.5.443: Flags [S], seq 1450214578, win 65535,
    options [mss 1460,nop,wscale 8,nop,nop,sackOK], length 0

There is no TS val in the options, which means timestamps are off, and the source port is above 49152.

What to do with a Linux result

If your current proxy shows a Linux fingerprint, you have a few options:

  • Match your user agent to Linux. This is consistent, but Linux desktop users are a small share of real traffic, so the combination stands out in its own way.
  • Run your own proxy and tune its kernel. The TTL, window, and timestamps can be set with sysctl, but the option order is fixed in the Linux kernel and needs packet rewriting to change.
  • Use a provider that sends a Windows fingerprint. Every Stat Proxies IP does this by default on every connection, at no extra cost. See how it works.

For background on why the fingerprint matters at all, read What is TCP/IP fingerprinting?

FAQ

How do I check my proxy's TCP/IP fingerprint?

Send a request through the proxy to https://tls.peet.ws/api/all and read the tcpip.tcp_syn block. It shows the TTL, window size, window scale, timestamps, and TCP option order the website received, which together identify the operating system.

Why does my proxy show a TTL of 50-something?

The proxy server runs Linux, which sends packets with a TTL of 64, and each network hop subtracts one. A Windows machine starts at 128, so it usually arrives somewhere between 110 and 125.

Does the TCP/IP fingerprint change between HTTP and SOCKS5?

No. Both are TCP connections opened by the same proxy server, so they carry the same fingerprint. UDP over SOCKS5 has no TCP handshake, so it has no TCP fingerprint.

Why does my TLS fingerprint change between curl and Python but my TCP fingerprint doesn't?

Through an HTTPS proxy, the TLS handshake still comes from your HTTP client, so each client has its own TLS fingerprint. The TCP connection to the website comes from the proxy server, so it is the same whichever client you use.

What TCP/IP fingerprint do Stat Proxies IPs send?

Windows 11, on every connection: TTL 128, a 65535 window with scale 8, the option order mss, nop, wscale, nop, nop, sackOK, no TCP timestamps, and source ports from 49152 up.