Back to Blog
Network

WebRTC IP Leak: What It Is and How to Prevent It

How WebRTC exposes your real IP address through ICE candidates, and how to prevent WebRTC leaks while keeping functionality intact.

Introduction

WebRTC is a browser technology that enables real-time communication: video calls, voice chat, peer-to-peer file sharing. To establish these connections, browsers use the ICE (Interactive Connectivity Establishment) protocol, which gathers network interface addresses and exchanges them with peers. The problem is that ICE collects your real IP addresses, including local network IPs and your public IP, and makes them available through JavaScript. This happens outside the normal HTTP proxy path, meaning a proxy alone does not protect you.

BotBrowser controls WebRTC ICE behavior at the browser engine level. Instead of disabling WebRTC entirely (which is itself a detectable signal), BotBrowser ensures that ICE candidates only contain IP addresses consistent with your proxy identity.

Privacy Impact

WebRTC IP leaks are one of the most well-known privacy gaps in browser technology. When a webpage initiates a WebRTC peer connection, the browser gathers ICE candidates from every available network interface. These candidates contain:

  • Local IP addresses: Your machine's private IP on the local network (e.g., 192.168.1.100)
  • Public IP address: Your real public IP from STUN server responses
  • IPv6 addresses: If available, your full IPv6 address

A website can collect these candidates through the RTCPeerConnection API without any user interaction or permission prompts. Even if you route all HTTP traffic through a proxy, WebRTC STUN requests travel directly to the STUN server, revealing your real public IP.

This creates a direct path to de-anonymization. A tracking system that sees your proxy IP in HTTP requests but your real IP in WebRTC candidates knows exactly what is happening. The inconsistency itself is a strong signal.

Technical Background

How ICE Candidate Gathering Works

When JavaScript creates an RTCPeerConnection, the browser begins gathering ICE candidates. This process involves:

  1. Host candidates: The browser enumerates local network interfaces and collects their IP addresses. These become "host" candidates.

  2. Server-reflexive candidates: The browser sends STUN (Session Traversal Utilities for NAT) binding requests to configured STUN servers. The STUN server responds with the public IP address it observes, creating "srflx" candidates.

  3. Relay candidates: If TURN (Traversal Using Relays around NAT) servers are configured, the browser establishes relay paths through them, creating "relay" candidates.

Each candidate type exposes different network information. Host candidates reveal local IPs. Server-reflexive candidates reveal your public IP. The combination of all three provides a detailed map of your network topology.

Why Extensions Cannot Fully Solve This

Browser extensions that attempt to control WebRTC have fundamental limitations:

  • Disabling WebRTC entirely prevents ICE gathering but creates a detectable fingerprint. Sites can check for the absence of RTCPeerConnection and flag it.
  • Blocking STUN requests at the extension level may miss requests initiated before the extension loads or from service workers.
  • Modifying ICE candidates through JavaScript monkey-patching is fragile. The real candidates are generated in native code before JavaScript has a chance to intercept them.

Engine-level control is the only approach that can modify ICE candidate generation at the source, before any JavaScript on the page can observe the original values.

Common Approaches and Their Limitations

Disabling WebRTC in Browser Settings

Some browsers allow disabling WebRTC through flags (e.g., media.peerconnection.enabled in Firefox). This stops all ICE gathering but also breaks legitimate WebRTC features like video conferencing. More importantly, the absence of WebRTC APIs is a distinct fingerprint signal. A browser that lacks RTCPeerConnection stands out.

WebRTC Control Extensions

Extensions like WebRTC Leak Prevent or uBlock Origin can restrict WebRTC to certain IP ranges. However, extensions operate in the JavaScript layer after the browser engine has already gathered candidates. They can intercept the onicecandidate callback, but the original candidates exist in memory. Some extension approaches also create detectable side effects: modified prototype chains, altered timing patterns, or missing properties on the RTCPeerConnection object.

VPN-Level Protection

A VPN routes all system traffic, including STUN requests, through the VPN tunnel. This prevents the STUN server from seeing your real IP. However, VPNs do not control local (host) ICE candidates. Your local network IP (e.g., 192.168.x.x) is still gathered and exposed. For some tracking scenarios, local IPs contribute to a stable identifier.

BotBrowser's Engine-Level Approach

BotBrowser modifies ICE candidate generation inside the Chromium networking stack. This is fundamentally different from all the approaches above:

  • WebRTC remains fully functional. RTCPeerConnection exists and works normally.
  • ICE candidates are generated with controlled IP information from the start.
  • No JavaScript-visible modifications exist on WebRTC objects.
  • STUN/TURN behavior is controlled at the network layer, not intercepted after the fact.

BotBrowser's Approach

The --bot-webrtc-ice Flag

BotBrowser's --bot-webrtc-ice flag (ENT Tier1) controls which ICE servers are used and what IP information appears in candidates:

# Use Google's STUN server (preset)
chrome --bot-profile="/path/to/profile.enc" \
       --proxy-server="socks5://user:pass@proxy:1080" \
       --bot-webrtc-ice="google"

# Use custom STUN and TURN servers
chrome --bot-profile="/path/to/profile.enc" \
       --proxy-server="socks5://user:pass@proxy:1080" \
       --bot-webrtc-ice="custom:stun:stun.example.com:3478,turn:turn.example.com:3478"

The google preset configures stun:stun.l.google.com:19302, which is the most commonly observed STUN configuration in regular Chrome browsers.

How It Works Together with Proxies

When combined with --proxy-server, BotBrowser ensures that:

  1. STUN requests route through the proxy, so the STUN server sees the proxy's IP
  2. ICE candidates contain only the proxy IP as the public address
  3. Local network IPs do not appear in host candidates
  4. The WebRTC fingerprint matches what a normal user behind that proxy would produce

UDP over SOCKS5 (ENT Tier3)

For ENT Tier3 users, BotBrowser supports SOCKS5 UDP ASSOCIATE. This tunnels QUIC traffic and STUN probes over the SOCKS5 proxy automatically when the proxy supports UDP:

chrome --bot-profile="/path/to/profile.enc" \
       --proxy-server="socks5://user:pass@proxy:1080"

No additional flags are needed. If the SOCKS5 proxy supports UDP ASSOCIATE, BotBrowser tunnels STUN traffic through it automatically.

WebRTC Configuration via Profile

WebRTC behavior can also be controlled through the profile's configuration:

# Use profile-defined WebRTC settings
chrome --bot-profile="/path/to/profile.enc" \
       --bot-config-webrtc=profile

# Disable WebRTC entirely (not recommended for consistency)
chrome --bot-profile="/path/to/profile.enc" \
       --bot-config-webrtc=disabled

The profile setting uses whatever WebRTC configuration the profile defines. The disabled setting turns off WebRTC, but this creates a detectable signal and should only be used when WebRTC functionality is genuinely not needed.

Configuration and Usage

Full Privacy Configuration (CLI)

Combine WebRTC protection with proxy, DNS, and fingerprint settings:

chrome --bot-profile="/path/to/profile.enc" \
       --proxy-server="socks5://user:pass@proxy:1080" \
       --bot-webrtc-ice="google" \
       --bot-local-dns \
       --bot-config-timezone="America/New_York" \
       --bot-config-locale="en-US"

Playwright Integration

const { chromium } = require('playwright-core');

(async () => {
  const browser = await chromium.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      '--bot-profile=/path/to/profile.enc',
      '--proxy-server=socks5://user:pass@proxy:1080',
      '--bot-webrtc-ice=google',
      '--bot-local-dns',
    ],
    headless: true,
  });

  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto('https://example.com');
  await browser.close();
})();

Puppeteer Integration

const puppeteer = require('puppeteer-core');

(async () => {
  const browser = await puppeteer.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      '--bot-profile=/path/to/profile.enc',
      '--proxy-server=socks5://user:pass@proxy:1080',
      '--bot-webrtc-ice=google',
      '--bot-local-dns',
    ],
    headless: true,
    defaultViewport: null,
  });

  const page = await browser.newPage();
  await page.goto('https://example.com');
  await browser.close();
})();

Verification

After launching BotBrowser with WebRTC protection:

  1. Visit a WebRTC leak test site (such as browserleaks.com/webrtc)
  2. Check that no local IP addresses (192.168.x.x, 10.x.x.x) appear in ICE candidates
  3. Verify that only the proxy IP appears as the public address
  4. Confirm that your real public IP is not visible in any candidate
  5. Test that WebRTC features still function (the API should be present and working)

You can also verify programmatically:

const candidates = await page.evaluate(() => {
  return new Promise((resolve) => {
    const ips = [];
    const pc = new RTCPeerConnection({
      iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
    });
    pc.createDataChannel('test');
    pc.onicecandidate = (e) => {
      if (e.candidate) {
        const match = e.candidate.candidate.match(/([0-9]{1,3}\.){3}[0-9]{1,3}/);
        if (match) ips.push(match[0]);
      } else {
        resolve(ips);
      }
    };
    pc.createOffer().then(o => pc.setLocalDescription(o));
  });
});
console.log('ICE candidate IPs:', candidates);

Best Practices

  1. Always combine --bot-webrtc-ice with --proxy-server. WebRTC protection is only meaningful when you are routing traffic through a proxy. Without a proxy, your real IP is already visible in HTTP requests.

  2. Use the "google" preset unless you have specific requirements. The Google STUN server is the most commonly observed configuration and produces the most natural-looking ICE candidates.

  3. Add --bot-local-dns for complete network privacy. WebRTC and DNS are the two most common network-level leak paths. Closing both ensures comprehensive protection.

  4. Do not disable WebRTC unless absolutely necessary. Disabling WebRTC creates a detectable fingerprint signal. BotBrowser's controlled approach keeps WebRTC functional while protecting your IP.

  5. Test after every configuration change. WebRTC behavior depends on the interaction between proxy, ICE server, and network configuration. Always verify after changes.

Frequently Asked Questions

Does BotBrowser break WebRTC video calls? No. WebRTC remains fully functional. Video calls, voice chat, and peer-to-peer connections all work normally. BotBrowser only controls which IP addresses appear in ICE candidates.

What is the difference between --bot-webrtc-ice and --bot-config-webrtc? The --bot-webrtc-ice flag controls ICE server configuration and IP exposure. The --bot-config-webrtc flag controls whether WebRTC uses profile settings, real system settings, or is disabled entirely.

Do I need --bot-webrtc-ice if I use a VPN? A VPN prevents STUN servers from seeing your real public IP, but local host candidates may still be exposed. BotBrowser provides more complete control over all candidate types.

Can websites detect that ICE candidates are being controlled? BotBrowser modifies candidate generation at the engine level. The ICE candidates look identical to those produced by a browser behind a proxy with that IP. There are no JavaScript-visible artifacts.

What about IPv6 leaks through WebRTC? BotBrowser controls both IPv4 and IPv6 candidate generation. IPv6 addresses from your real network interfaces do not appear in ICE candidates.

Does UDP over SOCKS5 require special proxy configuration? The proxy must support SOCKS5 UDP ASSOCIATE (RFC 1928). BotBrowser detects this capability automatically. No additional flags are needed beyond --proxy-server.

Summary

WebRTC IP leaks are a significant privacy concern that proxies alone do not address. BotBrowser controls ICE candidate generation at the browser engine level, ensuring that only proxy-consistent IP addresses appear in WebRTC candidates while keeping WebRTC fully functional.

For a complete network privacy configuration, combine WebRTC protection with Proxy Configuration and DNS Leak Prevention. For managing multiple identities with different network configurations, see Multi-Account Browser Isolation.

#webrtc#ip-leak#proxy#privacy#network