Skip to content
Anti-Bot

What Is a WebRTC IP Leak?

Pim

Pim · Scrappey Research

July 30, 2026 6 min read

What Is a WebRTC IP Leak? — conceptual illustration
On this page

A WebRTC IP leak is when your browser quietly reveals your real IP address — even though you set up a proxy to hide it. It is the most-overlooked failure mode in browser-based scraping in 2026. WebRTC (the browser feature behind video calls and peer-to-peer connections) finds your real local and public IP using STUN servers, and it does this even when all your HTTP traffic is routed through a proxy. The leak happens because WebRTC works at the network layer below the HTTP proxy — it talks directly to STUN servers from your real network interface, so the proxy never sees it. Anti-bots use this leaked IP as one input in a five-vector coherence test that all major vendors run.

Quick facts

How it leaksRTCPeerConnection ICE candidates expose local + STUN-discovered IPs
Bypassed proxy?Yes — HTTP proxy does not route STUN
Vendors checking itCloudflare, PerimeterX, DataDome, Akamai
Coherence testIP country + timezone + Accept-Language + WebRTC ICE + DNS resolver must all agree
Best fixCamoufox with geoip=True — auto-aligns all 5 vectors

How the leak works

WebRTC is the browser API for peer-to-peer audio, video, and data connections — think browser video chat. For two peers to connect when both sit behind a home router (NAT — the address translation that lets many devices share one public IP), WebRTC needs to discover the addresses they can be reached at. It does this with the ICE protocol, which gathers candidate IPs from three places: your local network interface, your public IP via STUN servers (servers whose only job is to tell you what your public IP looks like from the outside), and TURN relays. Any web page can run this:

const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
pc.createDataChannel('');
pc.createOffer().then(o => pc.setLocalDescription(o));
pc.onicecandidate = (e) => { if (e.candidate) console.log(e.candidate.candidate); };

The returned ICE candidates include your real IP, even if all your HTTP traffic is going through a proxy. The proxy hides your HTTP requests, but WebRTC went around it.

The 5-vector coherence test

Modern anti-bots check whether five different signals all tell the same geographic story. If you claim to be in one place, all five should agree:

  1. IP country — the country of your proxy's exit IP.
  2. Timezone — what the browser reports via Intl.DateTimeFormat().resolvedOptions().timeZone.
  3. Accept-Language header — your stated language preferences.
  4. WebRTC ICE candidate — the network the browser is actually connecting from (the leaked IP).
  5. DNS resolver location — which DNS server looked up the page's domain.

Picture a US proxy paired with an Accept-Language of ur-PK (Urdu, Pakistan), a timezone of Asia/Karachi, and a Pakistani WebRTC candidate — it fails immediately. It does not matter how good the proxy is; the contradiction between the vectors is itself the signal. This is why "use a US datacenter proxy and call it a day" stopped working around 2021.

Mitigation by tool

Camoufox with geoip=True looks up the country of your proxy exit IP, then sets timezone, locale, language, WebRTC ICE policy, and DNS to all match it. That one flag fixes the most common coherence failure in seconds. Playwright / Puppeteer need you to do this by hand — set locale, timezone_id, and Accept-Language yourself, and either disable WebRTC explicitly or route it through the proxy. HTTP scraping (curl_cffi, tls-client) has no WebRTC at all, so this vector never fires — part of why HTTP scraping beats browser scraping on many targets. Self-test: browserleaks.com/webrtc shows you exactly what WebRTC exposes from your setup. Run your browser context against it before you deploy.

The four candidate classes and what each one reveals

WebRTC discovers network paths by gathering ICE candidates, and the class of candidate matters as much as the address inside it. A probe reads the candidate list and checks its shape against what a real browser on a real network produces.

ClassWhat it isWhat its presence or absence says
hostA local interface addressModern browsers publish these as an mDNS name ending in .local, not a raw LAN address. A literal private address is an older or non-standard stack; a routable public address here is a misconfigured host
srflxThe public address a STUN server observedThis is the classic leak: it reports the real network egress even when the page was fetched through a proxy
relayAn address on a TURN relayForcing relay-only gathering and comparing the result against the page origin is a second, independent read of egress
noneGathering produced nothingSuppressing WebRTC entirely is itself anomalous - real consumer browsers gather at least one candidate

Two structural checks sit on top of the table. A server-reflexive candidate should have a host candidate backing it, since the reflexive address is discovered from a local interface; srflx with no host underneath indicates a filtered list rather than a real gathering run. And silence is not neutral - a browser that returns no candidates at all has removed a capability every ordinary browser has, which is a deviation in its own right.

SDP as an engine signature, and the egress coherence check

Beyond addresses, the session description a browser generates is itself an engine fingerprint. The set and ordering of RTP header extensions, the codec preference list, the exact formatting of the SDP offer, and which transport attributes appear are all implementation choices that differ between Chromium's and Firefox's WebRTC stacks. An offer that does not match the engine named in the User-Agent is a straightforward cross-API contradiction, and it is measured from a code path entirely separate from the JavaScript surfaces most modification targets.

The check that ties this together is egress coherence. The page arrived over some network path with an observable source address. WebRTC discovers its own path independently, through STUN or TURN, using a different socket and often a different protocol. Both should describe the same exit point. When the page origin resolves to one network and the reflexive candidate resolves to another, the browser is reaching the internet by two different routes - which is what a proxy applied at the HTTP layer but not the UDP layer looks like from the outside.

That same reasoning extends outward to locale. The exit address, the browser timezone, and the accepted languages are three independent claims about location, and a probe can compare all three - the mechanism described in timezone and IP mismatch. Consistency across them is a property of how the session was provisioned, not something a page script can arrange after the fact.

Handling this properly means routing every protocol through the same egress and provisioning locale to match, rather than disabling WebRTC and hoping the absence goes unnoticed. Infrastructure that owns both the browser and the network path can guarantee it; a browser flag cannot.

Code example

python
from camoufox.sync_api import Camoufox

# geoip=True aligns IP, WebRTC, DNS, timezone, and Accept-Language
# with the proxy exit country — fixes the 5-vector coherence test.
with Camoufox(
    headless=True,
    geoip=True,
    proxy={
        "server": "http://us-residential:port",
        "username": "user",
        "password": "pass",
    },
) as browser:
    page = browser.new_page()
    page.goto("https://browserleaks.com/webrtc")
    print(page.content())  # confirm no leak to your real IP

End of path

That is all 6 entries in Network-layer fingerprinting.

Signals emitted below the browser, by the TLS and HTTP stacks - before a single line of JavaScript runs.

Browse all topics

Related terms

Concept map

Concept map

How WebRTC IP Leak connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Anti-Bot
Building map…

Frequently asked questions

Does a VPN protect against WebRTC leaks?

It depends on the VPN — some force WebRTC traffic through the tunnel, but many do not. For scraping, do not rely on VPNs at all. Configure WebRTC at the browser layer instead (Camoufox geoip=True, or explicit Playwright settings) so the behaviour is predictable every time.

Can I just disable WebRTC entirely?

You can, but turning it off is itself a tell. Real browsers ship with WebRTC enabled, so a browser with no WebRTC stands out and creates a new anomaly. It is better to make WebRTC agree with your proxy than to switch it off.

Why does the proxy not route WebRTC?

HTTP proxies only carry HTTP and HTTPS traffic. WebRTC opens UDP connections (a faster, connectionless network protocol) straight to STUN servers from your real network interface, skipping the HTTP layer completely. SOCKS5 proxies can carry UDP, but most consumer scraping proxies are HTTPS-only.

How do I test my own setup?

Open browserleaks.com/webrtc using the same browser context your scraper mimics. The page lists every ICE candidate the browser would expose. If you spot your real public IP, or a local IP from a different country than your proxy's exit, you have a leak.

Is disabling WebRTC entirely a safe way to prevent an IP leak?

It prevents the leak but creates a different signal. Ordinary consumer browsers gather at least one ICE candidate, so a browser that returns none has a capability missing that essentially every real visitor has. Detection systems increasingly check that gathering produced a working candidate rather than only checking what the candidate contains. Routing WebRTC through the same egress as the page is the coherent answer; removing the API is a visible deviation.

Last updated: 2026-07-30 · Facts last verified: 2026-07-30