How to Prevent DNS and WebRTC Leaks When Using Proxies
A proxy only protects your identity if all traffic actually goes through it. DNS leaks and WebRTC leaks are the two most common ways traffic bypasses the proxy, exposing your real IP address or your DNS query patterns to the target or to third-party observers. Both leaks are silent -- your application works normally while your identity protection is compromised.
This guide explains how each leak type works at the protocol level, how to detect them, and how to configure your proxy setup to prevent them.
What Is a DNS Leak?
How DNS Resolution Normally Works with Proxies
When you send a request through a proxy, the domain name (e.g., example.com) must be resolved to an IP address at some point. Where that resolution happens determines whether a DNS leak exists.
No leak (proxy resolves DNS):
Your machine Proxy server Target
│ │ │
│ "Connect to example.com" │ │
│ ─────────────────────────> │ │
│ │ DNS: example.com? │
│ │ ─────> DNS Server │
│ │ <───── 93.184.216.34 │
│ │ │
│ │ ──────────────────────>│
│ <──────────────────────────│<───────────────────────│
Your machine sends the hostname to the proxy. The proxy resolves the DNS. Your ISP never sees the domain name.
DNS leak (client resolves DNS):
Your machine Proxy server Target
│ │ │
│ DNS: example.com? │ │
│ ──────> Your ISP's DNS │ │
│ <────── 93.184.216.34 │ │
│ │ │
│ "Connect to 93.184.216.34" │ │
│ ─────────────────────────> │ │
│ │ ──────────────────────>│
│ <──────────────────────────│<───────────────────────│
Your machine resolves the DNS locally before contacting the proxy. Your ISP's DNS server (or any configured DNS resolver) sees every domain you access. The proxy receives only an IP address, so it works normally -- but your DNS query history is exposed.
When DNS Leaks Occur
| Proxy Type | Default DNS Behavior | Leak Risk |
|---|---|---|
| HTTP proxy (plain HTTP target) | Proxy resolves DNS | Low |
| HTTP CONNECT (HTTPS target) | Depends on client implementation | Moderate |
| SOCKS4 | Client resolves DNS (protocol limitation) | High |
SOCKS5 (socks5://) | Client resolves DNS | High |
SOCKS5h (socks5h://) | Proxy resolves DNS | Low |
socks5:// and socks5h://. The h suffix stands for "hostname" -- it tells the client to send the hostname to the proxy for resolution rather than resolving it locally.
Detecting DNS Leaks
Method 1: Query logging test. Set up a DNS domain you control and make a request through your proxy to a subdomain of it. If your DNS server receives the query, the proxy is leaking DNS.
Method 2: Packet capture. Capture traffic on your local machine and look for outbound DNS queries (UDP port 53 or DoH to port 443):
# Capture DNS traffic while your scraper runs
# Look for queries to target domains -- if present, DNS is leaking
tcpdump -i any port 53 -w dns_leak_test.pcap &
TCPDUMP_PID=$!
# Run your proxy-using script
python your_scraper.py
# Stop capture
kill $TCPDUMP_PID
# Analyze: any queries to your target domains indicate a leak
tcpdump -r dns_leak_test.pcap -nn | grep -i "example.com"
Method 3: Use a DNS leak test service through your proxy:
import requests
proxies = {
"http": "http://USER:PASS@gate.hexproxies.com:8080",
"https": "http://USER:PASS@gate.hexproxies.com:8080",
}
# This service shows which DNS resolver made the query
response = requests.get("https://dnsleaktest.com/results", proxies=proxies)
# If the DNS server shown is YOUR ISP's resolver, DNS is leaking
# If it shows the PROXY'S resolver, no leak
Fixing DNS Leaks
Python requests with SOCKS5:
# WRONG: DNS leak -- client resolves DNS locally
proxies = {
"https": "socks5://USER:PASS@gate.hexproxies.com:1080",
}
# CORRECT: No DNS leak -- proxy resolves DNS
proxies = {
"https": "socks5h://USER:PASS@gate.hexproxies.com:1080",
}
Python aiohttp with SOCKS5:
from aiohttp_socks import ProxyConnector
# CORRECT: rdns=True forces remote DNS resolution
connector = ProxyConnector.from_url(
"socks5://USER:PASS@gate.hexproxies.com:1080",
rdns=True, # Remote DNS -- proxy resolves hostnames
)
Node.js with socks-proxy-agent:
const { SocksProxyAgent } = require('socks-proxy-agent');
// The socks5h:// scheme forces remote DNS resolution
const agent = new SocksProxyAgent(
'socks5h://USER:PASS@gate.hexproxies.com:1080'
);
cURL:
# WRONG: DNS leak with --socks5
curl --socks5 gate.hexproxies.com:1080 \
--proxy-user USER:PASS \
https://example.com
# CORRECT: No leak with --socks5-hostname
curl --socks5-hostname gate.hexproxies.com:1080 \
--proxy-user USER:PASS \
https://example.com
HTTP CONNECT proxies generally do not leak DNS because the client sends the hostname in the CONNECT request, and the proxy resolves it. However, some HTTP client libraries resolve the hostname locally before sending the CONNECT request. Verify with packet capture.
What Is a WebRTC Leak?
How WebRTC Exposes Your Real IP
WebRTC (Web Real-Time Communication) is a browser API for peer-to-peer audio, video, and data communication. To establish peer connections, WebRTC discovers all network interfaces on the machine and collects their IP addresses -- including your real public IP and local network IPs.
This information is available to any JavaScript running in the page, regardless of proxy settings:
// This code, running on any website, can discover your real IP
const pc = new RTCPeerConnection({
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
});
pc.createDataChannel("");
pc.createOffer().then((offer) => {
pc.setLocalDescription(offer);
});
pc.onicecandidate = (event) => {
if (event.candidate) {
// This string contains your REAL IP address
// even if you are using an HTTP/SOCKS5 proxy
console.log(event.candidate.candidate);
// Example output:
// "candidate:0 1 UDP 2122252543 192.168.1.100 54321 typ host"
// "candidate:1 1 UDP 1686052863 YOUR.REAL.PUBLIC.IP 54321 typ srflx"
}
};
The typ srflx candidate contains your public IP as discovered via STUN (Session Traversal Utilities for NAT). The typ host candidate contains your local network IP. Both leak information that is not routed through your proxy.
Why WebRTC Bypasses Proxies
Proxies operate at the application layer (HTTP, SOCKS). WebRTC's ICE (Interactive Connectivity Establishment) protocol operates at a lower level, using STUN/TURN servers to discover network connectivity. The browser sends UDP packets directly to STUN servers to discover its public IP -- these UDP packets bypass the proxy entirely because most proxies only handle TCP traffic.
┌──────────────────────────────────┐
│ Browser │
│ │
HTTP traffic ─────│── HTTP Proxy ─── Target website │
(proxied) │ │
│ │
WebRTC STUN ──────│── DIRECT to STUN server ──────> │
(NOT proxied) │ (bypasses proxy entirely) │
│ │
└──────────────────────────────────┘
When WebRTC Leaks Matter
WebRTC leaks only occur in browsers (or browser-like environments). They do not affect:
- Command-line HTTP clients (curl, wget)
- Python requests, aiohttp, httpx
- Node.js axios, got, node-fetch
- Any non-browser HTTP client
They do affect:
- Playwright, Puppeteer, Selenium (if WebRTC is not disabled)
- Chrome extensions
- Any headless or headed browser automation
Detecting WebRTC Leaks
Visit a WebRTC leak test page through your proxy-configured browser:
from playwright.sync_api import sync_playwright
def test_webrtc_leak(proxy_url):
"""Test for WebRTC leaks in a Playwright browser session."""
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={"server": proxy_url},
)
page = browser.new_page()
# Navigate to a WebRTC leak test
page.goto("https://browserleaks.com/webrtc")
# Wait for ICE candidates to be gathered
page.wait_for_timeout(5000)
# Extract the detected IPs
# If your real IP appears alongside the proxy IP,
# WebRTC is leaking
content = page.content()
print(content) # Check for your real IP in the output
browser.close()
Preventing WebRTC Leaks
Playwright:
from playwright.sync_api import sync_playwright
def create_leak_proof_browser(proxy_url):
"""Launch a Playwright browser with WebRTC disabled."""
p = sync_playwright().start()
browser = p.chromium.launch(
proxy={"server": proxy_url},
args=[
# Disable WebRTC entirely
"--disable-webrtc",
# Disable WebRTC multiple routes (prevents local IP leak)
"--disable-webrtc-multiple-routes",
# Force WebRTC to use proxy (if not disabling entirely)
"--force-webrtc-ip-handling-policy=disable_non_proxied_udp",
],
)
context = browser.new_context(
# Additional fingerprint consistency
locale="en-US",
timezone_id="America/New_York",
)
return p, browser, context
Puppeteer:
const puppeteer = require('puppeteer');
async function launchLeakProofBrowser(proxyUrl) {
const browser = await puppeteer.launch({
args: [
`--proxy-server=${proxyUrl}`,
// Disable WebRTC to prevent IP leaks
'--disable-webrtc',
'--disable-webrtc-multiple-routes',
// Alternative: force all WebRTC through proxy
'--force-webrtc-ip-handling-policy=disable_non_proxied_udp',
// Disable WebRTC event logging
'--disable-webrtc-hw-decoding',
'--disable-webrtc-hw-encoding',
],
});
return browser;
}
Selenium with Chrome:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
def create_leak_proof_driver(proxy_host, proxy_port, proxy_user, proxy_pass):
"""Create a Selenium ChromeDriver with WebRTC disabled."""
options = Options()
# Disable WebRTC
options.add_argument("--disable-webrtc")
options.add_argument("--disable-webrtc-multiple-routes")
options.add_argument(
"--force-webrtc-ip-handling-policy=disable_non_proxied_udp"
)
# Set proxy
options.add_argument(f"--proxy-server=http://{proxy_host}:{proxy_port}")
# WebRTC can also be disabled via Chrome preferences
prefs = {
"webrtc.ip_handling_policy": "disable_non_proxied_udp",
"webrtc.multiple_routes_enabled": False,
"webrtc.nonproxied_udp_enabled": False,
}
options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=options)
return driver
Browser extension approach (for manual browsing): Install a WebRTC leak prevention extension that overrides the WebRTC API to prevent IP discovery. Chrome's chrome://flags/#enable-webrtc-hide-local-ips-with-mdns flag also helps, but does not prevent the STUN-based public IP leak.
Additional Leak Vectors
DNS over HTTPS (DoH) Leak
Some applications and operating systems send DNS queries over HTTPS (DoH) to resolvers like Cloudflare (1.1.1.1) or Google (8.8.8.8). If your proxy is configured for regular HTTP traffic, DoH queries may still pass through the proxy -- but they go to the DNS resolver, not the target site. The DNS resolver sees your target domains.
Fix: Configure your system to use the proxy's DNS resolver, or disable DoH and ensure all DNS goes through the proxy (via SOCKS5h).
IPv6 Leak
If your machine has IPv6 connectivity and your proxy only handles IPv4, requests to IPv6-reachable targets may bypass the proxy entirely.
Fix:
# Disable IPv6 on Linux to prevent IPv6 leaks
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
In browser automation:
# Playwright: disable IPv6
browser = p.chromium.launch(
args=["--disable-ipv6"],
)
Canvas and Font Fingerprint "Leaks"
While not IP leaks, Canvas fingerprinting and font enumeration can uniquely identify your machine even through a proxy. These are not proxy leaks per se (your real IP is still hidden), but they can correlate your proxied sessions, reducing anonymity.
Fix: Use browser fingerprint randomization (canvas noise injection, font enumeration restriction) in browser automation. Libraries like playwright-stealth and puppeteer-extra-plugin-stealth include these protections.
Comprehensive Leak Prevention Checklist
DNS Leak Prevention
├── [ ] Use socks5h:// (not socks5://) for SOCKS5 proxies
├── [ ] Use HTTP CONNECT for HTTPS targets (standard behavior)
├── [ ] Verify with packet capture: no DNS on port 53 to ISP resolver
├── [ ] Disable DoH if not routed through proxy
└── [ ] Test with dnsleaktest.com through the proxy
WebRTC Leak Prevention (Browser Automation Only)
├── [ ] Add --disable-webrtc flag to browser launch
├── [ ] Add --force-webrtc-ip-handling-policy=disable_non_proxied_udp
├── [ ] Set webrtc.ip_handling_policy preference
├── [ ] Test with browserleaks.com/webrtc
└── [ ] Verify no STUN server connections in network log
IPv6 Leak Prevention
├── [ ] Disable IPv6 if proxy does not support it
├── [ ] Add --disable-ipv6 to browser launch flags
└── [ ] Verify no IPv6 connections in packet capture
General
├── [ ] Verify all traffic routes through proxy (tcpdump / Wireshark)
├── [ ] Test with ipinfo.io through proxy -- IP should be proxy IP
├── [ ] Check for DNS, WebRTC, and IPv6 leaks after every configuration change
└── [ ] Re-test after browser or library updates (defaults may change)
For more on proxy security fundamentals, see our DNS leak glossary entry, WebRTC leak glossary entry, and security best practices guide.
Frequently Asked Questions
Does Hex Proxies handle DNS resolution on the proxy server?
Yes. When using Hex Proxies via HTTP CONNECT or SOCKS5h, DNS resolution happens on our proxy servers. Your ISP never sees your target domain names. For SOCKS5 connections, use socks5h:// (not socks5://) to ensure remote DNS resolution.
Do DNS leaks affect web scraping success rates?
DNS leaks do not directly affect success rates -- your scraper still works. The risk is privacy: your ISP or network operator can see every domain you access, even though the actual page content goes through the proxy. For scraping operations where the target list is sensitive (competitive intelligence, market research), DNS leaks are a meaningful concern.
Can a website detect that I am using a proxy via DNS?
Not directly. The website sees the proxy's IP, not your DNS queries. However, sophisticated detection systems can correlate the IP's geographic location with the DNS resolver location. If your DNS queries go to a US resolver but the proxy IP is in Germany, this mismatch can be a signal. Remote DNS resolution (SOCKS5h) prevents this.
Are WebRTC leaks a concern for API scraping (no browser)?
No. WebRTC is a browser-only technology. If you are using Python requests, curl, axios, or any non-browser HTTP client, WebRTC leaks cannot occur. They only matter for Playwright, Puppeteer, Selenium, and similar browser automation tools.
Leak prevention is the last step of proxy security -- getting the proxy right is necessary but not sufficient if traffic bypasses it. Hex Proxies resolves DNS on our servers for all protocol types and supports SOCKS5h for explicit remote DNS resolution. ISP proxies start at $2.08/IP; residential at $4.25/GB. Explore plans or read our SOCKS5 setup guide.