How the leak works
WebRTC is the browser API for peer-to-peer audio, video, and data connections. To establish a connection between two peers behind NAT, WebRTC uses the ICE protocol, which gathers candidate IPs from the local network interface, a public IP via STUN servers, and TURN relays. Any web page can call:
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 5-vector coherence test
Modern anti-bots run a coherence check across five vectors that should tell the same geographic story:
- IP country — your proxy exit IP's country.
- Timezone —
Intl.DateTimeFormat().resolvedOptions().timeZone. - Accept-Language header — language preferences.
- WebRTC ICE candidate — the network the browser is actually on.
- DNS resolver location — which DNS server resolved the page domain.
A US proxy with Accept-Language ur-PK, timezone Asia/Karachi, and a Pakistani WebRTC candidate fails immediately. The proxy quality does not matter — the inconsistency itself is 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 proxy exit IP, then sets timezone, locale, language, WebRTC ICE policy, and DNS to match. This single flag fixes the most common coherence failure mode in seconds. Playwright / Puppeteer require manual configuration — set locale, timezone_id, Accept-Language, and disable WebRTC explicitly or route it via the proxy. HTTP scraping (curl_cffi, tls-client) has no WebRTC at all, so the coherence test on this vector does not fire — part of why HTTP scraping outperforms browser scraping on many targets. Self-test: browserleaks.com/webrtc reveals exactly what WebRTC exposes from your setup. Run your browser context against it before deploying.
