Privacy Tech

Puppeteer WebRTC Leak Prevention: 5 Vectors and What Each One Needs

BC

BotCloud Team

May 4, 2026·8 min read

Why "Disable WebRTC" Does Not Work

The first thing every WebRTC leak guide tells you is to set --disable-webrtc or its variants in Chromium. That recommendation has been wrong for years. Disabling WebRTC outright breaks Google Meet, Discord, Zoom Web, almost every video chat application, and a growing list of sites that use WebRTC for low-latency data channels (real-time multiplayer, collaborative editors, browser-based gaming).

Worse, it is detectable. A site that calls new RTCPeerConnection() and gets a thrown exception, or a missing API, knows the browser has tampered with the API. That is itself a fingerprint — the same class of leak that makes navigator.webdriver patches detectable.

Modern WebRTC leak prevention is not about disabling WebRTC. It is about controlling what WebRTC reveals through five distinct channels.

The Five Leak Vectors

When RTCPeerConnection gathers ICE candidates to negotiate a peer-to-peer connection, it produces multiple types of candidate, each leaking information through a different channel:

1. STUN — Public IP via Server-Reflexive Candidate

The browser contacts a STUN server (stun:stun.l.google.com:19302 is the public default). The STUN server responds with the IP address it sees the request coming from — which is your real public IP at the network layer, regardless of what HTTP proxy your browser is configured to use.

This is the headline WebRTC leak. The server-reflexive candidate (typ srflx) carries this address. Any web page that creates an RTCPeerConnection and listens for onicecandidate events sees it.

2. TURN — Relay Server Disclosure

If a TURN server is configured (typically by a video chat application), the relay candidate (typ relay) reveals the TURN server's IP. This is less of a privacy leak in itself, but its presence indicates a TURN server is being used, which can identify the application behind the connection.

3. Local IP — Host Candidate via Direct Interface

Without privacy mitigations, the host candidate exposes the raw private network address (192.168.x.x, 10.x.x.x, 172.16.x.x). Combined with browser-side fingerprinting, this lets a site distinguish between users behind the same NAT and track devices across networks.

4. IPv6 — Parallel Address Disclosure

If the browser has IPv6 connectivity, ICE candidates include the IPv6 address. IPv6 addresses are globally unique by design and frequently bypass IPv4-only proxy configurations entirely. Many "WebRTC leak prevention" guides cover only IPv4.

5. mDNS — Hostname-Based Side Channel

Chrome's privacy default replaces raw private IPs with a randomized .local hostname like 12345678-1234-5678-1234-567812345678.local. The mDNS hostname is only resolvable on the local network, so a remote site cannot translate it to an IP address. This is the protection that actually works against vector 3 by default in modern Chrome.

The catch: the mDNS hostname is generated per session and persists for the session's lifetime. It is itself a fingerprint surface that can be used for cross-page tracking within a single browsing session.

What an Actual Probe Reveals

Here is the output of a RTCPeerConnection probe run from inside a Playwright session against a Chromium build with default settings, using Google's public STUN server. The probe creates a peer connection, listens for ICE candidates, and logs each one:

[
  {
    "type": "host",
    "address": "12345678-1234-5678-1234-567812345678.local",
    "protocol": "udp",
    "candidateType": "host"
  },
  {
    "type": "srflx",
    "address": "YOUR_PUBLIC_IP",
    "protocol": "udp",
    "candidateType": "srflx"
  }
]

Two candidates, both UDP. Read against the five vectors:

  • Vector 3 (local IP): protected. The host candidate is the mDNS hostname, not the raw private IP. Chrome's default behavior is doing the right thing here.
  • Vector 1 (STUN public IP): leaking. The srflx candidate carries the real public IP — exactly the address the user is connecting from at the network layer. Any page that runs this probe sees it, regardless of whether the page itself is loaded through an HTTP proxy.

This is the typical state of a Chromium browser with no extra configuration. mDNS protects vector 3 by default; vectors 1, 2, 4, and 5 are still in their raw state.

What Each Chrome Flag Actually Fixes

Chromium ships several command-line flags related to WebRTC. The map of flag-to-vector is more confusing than it should be:

FlagWhat It DoesVectors Affected
--force-webrtc-ip-handling-policy=default_public_interface_onlyRestricts ICE candidates to the default public interfaceMitigates vector 3 (private interface), does not affect vector 1 (STUN reflection)
--force-webrtc-ip-handling-policy=disable_non_proxied_udpForbids UDP candidates that are not proxied through the configured proxyMitigates vectors 1, 2, 4 — but only if a proxy is configured AND the proxy supports UDP (most HTTP proxies do not)
--disable-features=WebRtcHideLocalIpsWithMdnsRemoves the mDNS protection (raw private IPs come back)Reverses vector 5 protection — almost never what you want
--disable-webrtcDisables WebRTC entirelyBreaks legitimate WebRTC use cases and is detectable

The flag combination most guides recommend (--force-webrtc-ip-handling-policy=disable_non_proxied_udp plus a SOCKS5 proxy) is correct in principle but fails in practice on most stacks because the SOCKS5 implementation does not handle UDP. Standard SOCKS5 supports UDP via the UDP ASSOCIATE command, but most HTTP proxy clients ship with TCP-only SOCKS5 and silently fall back to direct UDP — which leaks vector 1 anyway.

What "UDP over SOCKS5" Actually Means

A SOCKS5 proxy can carry UDP traffic, but only if both the client and the server implement the UDP ASSOCIATE extension. The client side (the browser) must:

  1. Open a TCP control connection to the SOCKS5 server.
  2. Issue a UDP ASSOCIATE request.
  3. Receive a UDP relay endpoint from the server.
  4. Wrap each outbound UDP packet in the SOCKS5 UDP header before sending it to the relay.
  5. Unwrap inbound packets from the relay before delivering them to WebRTC.

Most browsers and most proxy client libraries do not do this. They open the SOCKS5 control connection, authenticate, and then route TCP traffic. UDP traffic — including STUN, TURN, and the actual media plane of WebRTC — bypasses the proxy and goes out the default network interface.

The fix at the browser engine level is to integrate UDP-over-SOCKS5 into the network stack so that any WebRTC ICE candidate gathered through the proxy actually flows through the proxy. When this is in place, the srflx candidate carries the proxy's IP, not the local network's public IP. The leak is closed at vector 1 because the STUN server sees the proxy.

A Verification Checklist

When auditing your WebRTC privacy posture, run through these in order:

  1. Run the ICE candidate probe. Use a script that creates new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }), listens for onicecandidate, and logs every candidate. Read each candidate's type and address. You learn more from this than from any aggregator site.

  2. Compare srflx address to your expected public IP. If you are using a proxy, the srflx address should be the proxy's IP. If it is your real ISP-assigned IP, your stack is leaking vector 1.

  3. Check for IPv6 candidates. If your network has IPv6 connectivity, look for srflx candidates with IPv6 addresses. These are often carried even when IPv4 is proxied. Either disable IPv6 at the OS level or use a proxy that supports IPv6.

  4. Confirm host candidates are mDNS hostnames. Look for the .local suffix. If you see raw 192.168.x.x or 10.x.x.x addresses, mDNS protection is off — check that you do not have --disable-features=WebRtcHideLocalIpsWithMdns set anywhere.

  5. Verify with a leak test page. browserleaks.com/webrtc and ipleak.net are useful aggregators. Note that "No Leak" verdicts on these sites compare your WebRTC IP to your apparent connection IP. If you are using a proxy, "No Leak" means WebRTC and HTTP show the same IP, not that your real IP is hidden — the test only catches WebRTC bypassing the proxy.

  6. Test the actual application path. Open Google Meet, start a call, and inspect the negotiated ICE candidates in chrome://webrtc-internals. The negotiated candidate is what the remote peer sees. This is the ground truth for what your real privacy posture is.

Quick FAQ

Q: Does SOCKS5 prevent WebRTC leak?

It depends on whether your SOCKS5 implementation supports UDP. Most do not. A SOCKS5 proxy that only handles TCP will route HTTP through the proxy but let UDP (and therefore WebRTC) bypass it. The leak prevention requires SOCKS5 with UDP ASSOCIATE support, integrated into the browser's network stack.

Q: What are the 5 WebRTC leak vectors?

STUN (server-reflexive public IP), TURN (relay server disclosure), local IP (host candidate raw), IPv6 (parallel address), and mDNS (hostname tracking surface). Each has its own mitigation; no single flag fixes all of them.

Q: Why does --disable-webrtc not work?

It breaks legitimate sites that need WebRTC (video chat, real-time multiplayer, collaborative tools). It is also detectable — a site can probe RTCPeerConnection availability and identify a tampered browser.

Q: Does --force-webrtc-ip-handling-policy=disable_non_proxied_udp close the leak?

It instructs Chromium to reject UDP candidates that do not go through the proxy, which addresses vector 1 in principle. But it requires the proxy to actually support UDP, which most do not. Without UDP-capable proxy support, the flag either silently falls back to direct UDP or fails to gather candidates entirely.

Q: Is the mDNS hostname a fingerprint?

Yes. Chrome generates a per-session random hostname that persists for the session lifetime. A site can read it and use it as a session-scoped tracking ID. It is still better than exposing the raw private IP, which would persist across sessions.

Bottom Line

WebRTC leak is not one channel. It is five, and each channel needs a different fix. mDNS gets vector 3 most of the time. Vectors 1, 2, and 4 require a proxy that supports UDP, integrated at the browser's network stack so that ICE candidate gathering actually flows through the proxy. Vector 5 is intrinsic to the mitigation for vector 3 and only matters at the session-scope level.

Most of the "WebRTC leak prevention" guidance in circulation simplifies this to "use a SOCKS5 proxy" or "set this Chrome flag" — both correct in principle and both incomplete in practice. If you need a Chromium build with UDP-over-SOCKS5 in the network stack and per-session ICE control, BotCloud provides a managed engine-level implementation.

#webrtc#leak-prevention#puppeteer#playwright#proxy#socks5

Share this post