iplogs.com

How VPN Detection Actually Works — The Research-Backed 7-Layer Method

The 7 layers a modern VPN detector combines: IP intel, TCP/TLS fingerprints, SNITCH RTT (NDSS 2025), active probing, client signals. With citations.

·18 min readVPN detectionJA3SNITCHfingerprinting

There is a lot of folklore about how VPN detection works. Most of it is wrong, or at least ten years out of date. This post walks through the seven detection layers that a modern research-grade service combines — grounded in peer-reviewed papers and production experience building IPLogs. Every layer is citation-backed.

Layer 1 — IP intelligence

Before any fancy fingerprint analysis, you ask the cheapest possible question: is this IP on a list? Three lists carry most of the weight:

  • Commercial VPN provider lists. NordVPN (AS212238), Mullvad (AS397397), Private Internet Access (AS55286), ProtonVPN (AS209103), SoftEther (AS36599) all have registered ASNs. Any IP in one of those ASNs is almost always a VPN exit.
  • Datacenter and hosting ASNs. A real end-user device almost never originates from AWS, Google Cloud, Azure, DigitalOcean, Hetzner, OVH, Vultr, or Leaseweb. The dc_ip signal alone has a low weight because CDNs and legitimate cloud workloads also live there — but it combines usefully with other layers.
  • Tor exit list. The Tor Project publishes the current exit-node list at check.torproject.org/exit-addresses, refreshed every few minutes. Refresh hourly; any IP on the list is a Tor exit with near-perfect confidence.

IP intelligence is the highest-precision layer but also the easiest to evade (for an operator: rent residential IPs). It is a necessary floor, not a ceiling.

Layer 2 — TCP/IP fingerprinting

The TCP/IP stack a host exposes reveals its operating system and often its role. An iPhone emits a specific TTL (64), TCP window size (65535 with scaling), and MSS (1460 on Ethernet). A Linux datacenter box emits a different set. Commercial VPN servers often run Linux and forward traffic through a kernel tunnel, so the exposed stack characteristics are Linux-default even when the client advertises iOS or Windows. Three signals that fire reliably:

  • mtu_anomaly — MSS is not one of the standard 1460/1440/1380 values, consistent with a tunnel wrapping the inner payload.
  • ttl_os_mismatch — TTL is 63 instead of 64, meaning one extra routing hop in the form of a VPN server.
  • tcp_window_anomaly — Initial TCP window is not the kernel default for the claimed OS.

Layer 3 — TLS / JA3 / JA4 fingerprinting

The TLS ClientHello records a large amount of client-specific preference: cipher suites in a specific order, elliptic curves, TLS extensions, ALPN strings, supported versions. JA3 (2017) and JA4 (2023) take a deterministic hash of that ClientHello. Two clients that are byte-identical produce the same hash; two clients from different libraries produce different hashes. OpenVPN over TCP/443 emits a JA3 hash that no browser in the world produces. See our post on JA3 and JA4 explained for the full breakdown.

Layer 4 — RTT analysis (SNITCH, NDSS 2025)

The SNITCH paper from NDSS 2025 introduced a neat detection primitive: the TCP handshake RTT and the TLS handshake RTT should be almost identical on a direct connection. If there is a VPN server in between, the TLS handshake has to traverse the VPN tunnel but the TCP handshake only reaches the tunnel ingress, and the differential reveals the extra hops.

The SNITCH paper reports a true-positive rate above 95% on commercial VPN endpoints at a per-connection false-positive rate below 1% on residential ISPs — from a single RTT pair.

IPLogs implements SNITCH by measuring the handshake timings in the browser via PerformanceResourceTiming, re-sending them to the detection engine, and applying the paper's statistical test. There is also a related cross-layer RTTcheck that compares the TCP RTT against the expected geo-distance to the claimed IP location — a VPN user claiming to be in Frankfurt but whose TCP RTT says they're two hops from Chicago raises ageo_rtt_mismatch signal.

Layer 5 — Active probing

This is where things get spicy. When the passive layers raise suspicion, you can actively probe the suspected endpoint on well-known VPN ports. The GFW pioneered this technique for censorship; it works equally well for defensive detection.

  • OpenVPN HARD_RESET — send an OpenVPN protocol reset packet to UDP/1194 and watch for the response pattern. A real OpenVPN server responds with its own HARD_RESET. A random host drops or RSTs. Yields a high-confidence active_probe_openvpnsignal.
  • WireGuard handshake init — send a WireGuard MESSAGE_INITIATION packet to UDP/51820. A real WireGuard peer responds with a handshake response; anything else drops.
  • IKEv2 SA_INIT — send a small IKE SA_INIT on UDP/500. Real VPN servers respond with a corresponding SA_INIT_RESPONSE.
  • REALITY cert-switch — REALITY is an obfuscation scheme used by modern anti-GFW proxies. It mimics a real website until an authorized client fuzzes the SNI, at which point the server switches to a different certificate. SNI fuzzing is a cheap detection.

Active probing has ethical considerations: you are sending network packets to a third party. Services that implement active probing should rate-limit aggressively and honor opt-out requests.

Layer 6 — Client-side signals

If you have a browser on the line, the browser tells you things the network does not. Three useful cross-checks:

  • Timezone vs IP geo. Browser reports America/New_York but IP says Frankfurt: strong VPN signal.
  • Language vs region. Browser reports en-US but IP is in Taipei: mildly suspicious, depending on the population.
  • WebRTC ICE leak. Opening an RTCPeerConnectiontriggers ICE gathering, which sends the browser to a STUN server. The STUN server returns the browser's real public IP (the one NAT uses). If that IP differs from the HTTP source IP, the browser is behind a VPN that does not tunnel UDP.

All three are cheap to collect and easy to spoof, so they are corroborating signals — not primary ones.

Layer 7 — Port and network sanity

The last layer is a grab-bag of heuristics: services on non-standard ports, reverse DNS that does not resolve back, hosting-provider naming patterns in the PTR record, small fleet sizes for the reported city. Individually low-weight but useful for confidence bumping.

How to combine the seven layers

Each signal is assigned a weight between 0.05 and 0.95. Matched signals are summed into a final 0-to-1 score. Thresholds produce one of four verdicts: clean below 0.3, suspicious 0.3–0.5, vpn_likely 0.5–0.75, vpn_detected at or above 0.75. A single high-weight signal (like tor_exit at 0.95) can push a verdict to detected on its own; typically the detected verdicts come from two or three corroborating mid-weight signals.

The allowlisting layer matters more than most engineers realize: major CDNs (Cloudflare, Akamai, Fastly, Google, Microsoft, Apple, Meta, Netflix) and public DNS anycast addresses must be explicitly excluded to prevent the datacenter-ASN layer from producing false positives on real end users. Getting allowlisting wrong is the single biggest source of false-positive complaints in production detection services.

Try the pipeline

Every piece described above is deployed in IPLogs. Paste any IP, read the matched-signal list, and you'll see each layer's contribution to the verdict.

Check any IP against the 7-layer pipeline

The detection methods described above are all available through the IPLogs public API, free, no signup required.

Try the IP checker →