A user connects to your site through a VPN. Their HTTP requests come from the VPN’s exit IP — good, the VPN is doing its job. Then a JavaScript call grabs the user’s actual IP and sends it to your server. Wait, what?
This is the WebRTC IP leak: a browser API that can expose a user’s real IP address, including the private LAN IP, even when they’re using a VPN or proxy. It’s been known since 2015 and is still a real concern in 2026. This post explains how the leak works, why the API exists, what it actually reveals, and how to handle it — either by exploiting it for legitimate fingerprinting or by preventing it from your own services.
What WebRTC Is
WebRTC (Web Real-Time Communications) is the browser API for peer-to-peer video, audio, and data. It powers Google Meet, Zoom web client, Discord voice chat, and countless other applications.
For peer-to-peer to work, two browsers need to find a path to talk directly. The standard solution is ICE (Interactive Connectivity Establishment), which gathers a list of every plausible IP/port pair the browser could use for the connection:
- The browser’s local IPs on every network interface (LAN, Wi-Fi, etc.)
- The public IP as seen from a STUN server
- The TURN server’s IP as a fallback relay
This list of “candidates” is then offered to the other peer. The peer tries each candidate until one works.
The Leak
The privacy problem: gathering ICE candidates reveals the user’s IPs to JavaScript on the page. A web page can:
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] })
pc.createDataChannel('')
pc.createOffer().then(offer => pc.setLocalDescription(offer))
pc.onicecandidate = e => {
if (!e.candidate) return
console.log(e.candidate.candidate)
// Example output:
// candidate:1 1 UDP 2122252543 192.168.1.42 54321 typ host
// candidate:2 1 UDP 1686052607 203.0.113.5 54321 typ srflx raddr 192.168.1.42 rport 54321
}
The first candidate is the browser’s local LAN IP (192.168.1.42). The second is the public IP as seen from STUN (203.0.113.5). If the user is on a VPN, the public IP here is often the user’s real ISP IP, not the VPN’s IP — because STUN uses UDP, and some VPN configurations don’t route UDP correctly.
The site has no business knowing either of these. WebRTC needed them for peer-to-peer; the side-effect is that JavaScript on the page learns them too.
Why the Leak Exists
WebRTC has to enumerate IPs to make peer-to-peer work — that’s the design goal. Initial implementations exposed everything to JavaScript without restriction. Privacy advocates pointed this out; browsers gradually clamped down. In 2026 the situation is:
- Local IPs are largely hidden behind a randomized mDNS hostname like
7a8f9c3d.local. JavaScript sees the hostname, not the raw IP, unless the user has explicitly granted media permissions. (Chrome since 76, Firefox since 67.) - Public IP via STUN is still gathered and visible to JavaScript by default. If the user is on a VPN and the VPN doesn’t route UDP, the public STUN IP is the real ISP IP.
- Browser settings and extensions can disable WebRTC entirely or restrict ICE candidate gathering — but this is opt-in, not default.
The leak in 2026 is narrower than it was in 2015, but it isn’t gone.
What the Leak Actually Reveals
Depending on browser version and configuration:
| Field | Pre-2019 | 2026 default |
|---|---|---|
| Public IP (from STUN) | Yes | Yes |
| Real IP behind VPN | Yes (if VPN doesn’t route UDP) | Yes (if VPN doesn’t route UDP) |
| LAN IP (e.g., 192.168.1.x) | Yes | Hidden via mDNS unless media permission granted |
| Hostname / device name | No | No |
| MAC address | No | No |
The most concerning surviving leak is the real public IP when behind a poorly-configured VPN. UDP routing matters: a VPN that doesn’t tunnel UDP traffic to its STUN destinations exposes the original IP.
Detecting the Leak as a Defender
For fraud detection, a WebRTC-revealed IP is a strong signal. Trick: collect both the HTTP IP and the WebRTC IP, compare them.
function getWebRtcIp() {
return new Promise((resolve) => {
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
})
pc.createDataChannel('')
pc.createOffer().then(o => pc.setLocalDescription(o))
const ips = new Set()
pc.onicecandidate = e => {
if (!e.candidate) {
resolve(Array.from(ips))
return
}
const match = e.candidate.candidate.match(/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/)
if (match && !match[1].startsWith('192.168') && !match[1].startsWith('10.')) {
ips.add(match[1])
}
}
})
}
Compare to the HTTP-side IP your server sees. If they differ significantly (different ASN, different country), the user is probably behind a VPN or proxy. This is a high-quality fraud signal — much more reliable than just looking at the HTTP IP.
A few caveats:
- Some legitimate users have multi-interface setups (corporate VPN + home internet) where the two IPs differ for benign reasons.
- Some browsers and extensions block WebRTC entirely — the absence of WebRTC IPs is itself a signal of someone trying to hide.
- Mobile networks may not return useful WebRTC IPs.
Preventing the Leak (as a user)
If you’re a user worried about WebRTC leaks:
Use a proper VPN
Modern commercial VPNs (Mullvad, Proton, ExpressVPN) handle WebRTC by ensuring UDP traffic is also tunneled. STUN queries go through the VPN, so the IP seen by STUN is the VPN’s IP, not yours.
Avoid amateur or free VPNs that only tunnel HTTP/HTTPS. They leak via STUN.
Disable WebRTC in the browser
- Firefox: Set
media.peerconnection.enabledtofalseinabout:config. - Chrome: Use an extension like “WebRTC Network Limiter” or “uBlock Origin” (advanced settings).
- Safari: No exposed setting; relies on system-level VPN routing.
Note: disabling WebRTC breaks Google Meet, Zoom web client, and similar real-time apps. Most users don’t want this.
Use a browser with privacy defaults
The Tor Browser disables WebRTC by default. Brave has built-in WebRTC IP-handling settings.
Preventing the Leak (as a developer)
If you’re building a web application and want to not leak users’ IPs via WebRTC:
Don’t initialize WebRTC unless you need it
Just creating an RTCPeerConnection triggers IP enumeration. If your app doesn’t use real-time peer communication, don’t import or initialize the API.
Use TURN-only mode for peer connections
For WebRTC apps that need to work but want to minimize IP exposure, force traffic through a TURN relay server — the destination peer only sees the TURN server’s IP. You can configure this with iceTransportPolicy: 'relay'.
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'turn:turn.example.com', username: 'x', credential: 'y' }],
iceTransportPolicy: 'relay'
})
Performance is slightly worse (relay adds a hop), but the user’s IP is shielded.
Don’t surface ICE candidates to third parties
If your real-time app passes ICE candidates through a signaling server, the signaling server (and anyone with access to it) sees user IPs. Architect the signaling layer with appropriate access controls.
Should You Care?
For most application developers, the practical question is: do you exploit the WebRTC leak for fraud detection, or do you not use it at all?
The arguments for using it:
- Strong fraud signal. Mismatched WebRTC and HTTP IPs are a reliable proxy/VPN indicator.
- Hard to spoof. WebRTC runs in the user’s browser, not in headers — automation tools have to do extra work to fake it.
- Browser-supported. No special permissions needed for the public-IP component.
The arguments against:
- Privacy implications. You’re extracting data the user didn’t consciously share.
- Detection ethics. Many users use VPNs for legitimate privacy reasons; using WebRTC to defeat their privacy is a strong choice.
- GDPR exposure. Under GDPR, IP addresses are personal data; WebRTC-fingerprinting users without disclosure could be a legal issue.
A middle path: use WebRTC-derived IP only for high-stakes anti-fraud (large purchases, account creation under specific risk scores) where the benefit outweighs the privacy cost, and disclose it in your privacy policy.
WebRTC vs HTTP IP: A Summary
| Source | What you see | Reliability | Spoofable? |
|---|---|---|---|
| HTTP IP (req.ip) | The connection’s source — VPN if used | Very reliable | No (without VPN) |
X-Forwarded-For | What proxies report | Trustworthy only behind controlled proxies | Yes (open) |
| WebRTC public IP | Real IP if VPN doesn’t tunnel UDP | High when present | Difficult |
| Geolocation lookup | Approximate location | High at country level | Indirect (via VPN) |
For high-confidence fraud detection, combine all four. The HTTP IP gives you the connection-layer view; WebRTC IP can reveal a VPN; geolocation gives geographic context.
The Ip2Geo API returns the rich geo, ASN, and network-classification data on top of whichever IP you submit — you can run the same lookup on both the HTTP IP and the WebRTC IP and compare results.
TL;DR
- WebRTC leaks the user’s IP to JavaScript on the page, including (sometimes) the real IP behind a VPN.
- Modern browsers hide the LAN IP by default but still expose the public IP.
- VPNs that don’t tunnel UDP leak the real IP via STUN.
- As a user, use a quality VPN, or disable WebRTC in your browser if you don’t need it.
- As a developer, don’t initialize WebRTC unless you need it; use TURN-only mode if you do.
- For fraud detection, comparing WebRTC IP against HTTP IP is a strong signal — but consider the privacy implications.
- Under GDPR, treat WebRTC-derived IPs as personal data subject to the same rules as any other IP.
WebRTC is a powerful API that browsers couldn’t ship without exposing some IP information. The leak isn’t a bug; it’s a privacy trade-off built into how the protocol works. In 2026 the trade-off is better managed than a decade ago, but it’s still a trade-off — and one that matters whether you’re a user, a privacy-conscious developer, or a fraud team using every available signal.