WebRTC is the browser API that powers peer-to-peer video calls, screen sharing, and increasingly any web app that needs low-latency UDP. It was added to Chrome and Firefox starting in 2011 to enable browser-native conferencing without plugins. The protocol works extremely well for what it was designed for.
The IP leak comes from the part of WebRTC that has to discover what address the browser can be reached at, so that the other side of the call can connect back. That discovery process happens automatically, runs at page load with no user interaction, and any JavaScript on the page can read the results.
How the Leak Works: ICE and STUN
Establishing a peer-to-peer connection through firewalls and NATs is a hard problem. WebRTC solves it with the Interactive Connectivity Establishment (ICE) framework — RFC 8445 — which collects every possible candidate address for the browser and lets both sides try them all in parallel until one works.
The candidates ICE collects include:
- Host candidates — every local network interface the OS exposes. Ethernet, Wi-Fi, Bluetooth tethering, VPN adapters, and yes, the original interface that the VPN sits on top of.
- Server reflexive candidates — your public IP as seen from a STUN server on the internet. The browser sends a UDP packet to a public STUN server and the server replies with the source address it observed.
- Relayed candidates — TURN server addresses, used when peer-to-peer fails.
Crucially, ICE candidate gathering uses raw UDP to the STUN server. If your VPN routes only TCP and DNS, or if the OS has interfaces that the VPN does not have an explicit route over, the STUN packet leaks around the VPN through the underlying physical interface. The reflexive candidate is then your real ISP-assigned address, exposed to any JavaScript that calls new RTCPeerConnection().
The full PoC fits in a couple of lines of JavaScript: instantiate an RTCPeerConnection, create a data channel, call createOffer, and read the ICE candidates as they fire. The page never has to connect to anything. No user prompt. No permission. Candidates surface as plain SDP strings the page can parse.
What Your VPN Does and Does Not Fix
Most consumer VPNs route IP traffic at the OS level. They install a virtual network interface and add a default route through it. Traffic that goes through the kernel's routing table follows the new default route and exits through the VPN's server.
WebRTC's host candidate gathering, however, asks the OS for all network interfaces, including the one underneath the VPN. The VPN cannot make that interface invisible to ICE — the only way to do so would be to physically disable it, which would break the VPN itself.
A VPN with a properly enforced kill switch will prevent the leak in two ways:
- If the kill switch firewalls all non-VPN traffic, the STUN packet over the physical interface gets dropped, so no reflexive candidate is gathered.
- Even if a candidate is gathered, the peer connection itself cannot establish through the firewall, so the leaked IP cannot be used to circumvent the VPN.
But the leak still happens at the candidate-gathering stage if you do not have the kill switch on. The page still learns your IP. Whether the VPN later prevents the connection is irrelevant for surveillance purposes — the address has already been exfiltrated to the page's JavaScript and can be sent to any server via a plain HTTP request.
The mDNS Mitigation
Browser vendors became aware of this leak around 2014, and Chrome shipped a mitigation in version 76 (mid-2019) that hides local host candidates behind randomly generated .local mDNS hostnames. Instead of exposing 192.168.1.4 directly, Chrome generates an opaque identifier like e3c0f1c1-...-.local and uses ICE-level mDNS lookups to resolve it during call setup.
This eliminates the local IP leak in many cases. It does not address the reflexive candidate (the public IP from STUN), which remains the more privacy-sensitive value for most users. Firefox shipped a similar mitigation later.
Both mitigations only apply when the page does not have an active call to a real WebRTC peer. Once a connection is being negotiated for legitimate reasons — a video conference, a WebRTC-based VoIP app — the full IP set is exchanged with the remote peer. There is no clean way to have peer-to-peer WebRTC without exposing addresses to whoever you are calling.
What Actually Blocks the Leak
The options, in roughly increasing order of effectiveness and friction:
| Approach | Effectiveness | Side effects |
|---|---|---|
| uBlock Origin "Prevent WebRTC from leaking local IPs" setting | Local IPs only | None |
| Browser flag to disable non-proxied UDP | Blocks STUN over physical interface | Breaks browser-based calling apps |
| Disable WebRTC entirely (Firefox: media.peerconnection.enabled = false) | Complete | Breaks WebRTC entirely |
| VPN with strict kill switch over the same NIC | Blocks reflexive candidate | VPN downtime = no internet |
| Tor Browser (no WebRTC by default) | Complete | See Tor vs VPN |
The most pragmatic configuration for a desktop user who wants WebRTC video calls and does not want the IP leak: use a VPN with a verified kill switch (Mullvad, IVPN, and Proton VPN have well-tested ones), keep the kill switch on, and accept that participating in any actual peer-to-peer call will reveal your VPN exit IP to the other party.
Mobile Is Worse
Mobile browsers and apps complicate the picture because the OS often has multiple active network interfaces simultaneously (Wi-Fi, cellular, tethering) and routing decisions are made per-flow rather than globally. A mobile VPN that does not aggressively firewall non-VPN interfaces will leak through cellular even when Wi-Fi traffic is routed through the tunnel.
iOS limits low-level networking access for VPN apps in ways that can interact badly with WebRTC. Android's situation depends heavily on whether the VPN uses the "always-on" + "block connections without VPN" pair of settings, which Android exposes globally and which most VPN apps document.
The Bigger Lesson
WebRTC IP leaks are a particular instance of a recurring pattern: a browser API exposes capability for a legitimate use case, the capability is then queryable from any web page, and the capability turns out to imply more than its designers intended. WebGL fingerprinting, AudioContext fingerprinting, and font enumeration are all variations of the same theme — see our deep dive on browser fingerprinting.
The path forward is the slow shift toward permission-gated APIs: features that require explicit user consent before exposing any data. WebRTC is a poor candidate for that retrofit because the very thing that makes it useful — connecting quickly to a peer without UI friction — is what makes the leak silent. The realistic mitigation is the kill switch and the awareness, not a clean API redesign.