# WebRTC not working with a proxy: why calls fail

> WebRTC sends media over UDP, which an HTTP or SOCKS proxy in Chrome does not carry. With non-proxied UDP blocked, only a TURN relay over TCP is left.

Source: https://mosaicbrowser.com/blog/webrtc-calls-behind-a-proxy

## Why video calls struggle in a proxied profile

WebRTC sends media over UDP, which an HTTP or SOCKS proxy in Chrome does not carry. With non-proxied UDP blocked, only a TURN relay over TCP is left.

![0: Candidates gathered with non-proxied UDP blocked, beside a screenshot: a Mosaic leak test: every surface a site reads, checked from inside the profile page](https://mosaicbrowser.com/covers/webrtc-calls-behind-a-proxy.webp)

A video call that will not connect in a proxied browser profile is usually the leak protection working. WebRTC sends audio and video over UDP, and the proxies Chrome supports, HTTP and SOCKS, carry TCP. So once WebRTC is forbidden to use UDP outside the proxy, the only way a call can work is a TCP connection through the proxy to a relay the call service runs, called a TURN server. No relay reachable that way, no call.

## Why WebRTC does not work with a proxy

A browser sets up a WebRTC call by gathering candidates: addresses the other side might reach it on. Left alone, Chrome offers a host candidate, which is the machine's LAN address hidden behind a random `.local` name, and a server-reflexive one, which is the public address a STUN server saw the UDP packet come from. Then both sides try pairs of candidates until one gets through, and media flows over UDP between them.

None of that goes through the proxy. A STUN request is a UDP packet sent straight from the computer, so the address it learns is the computer's own public address, not the proxy's exit. That is the classic WebRTC leak: a page behind a proxy asks for candidates and reads the real address out of the answer.

## What disable_non_proxied_udp leaves

Chromium has one switch for this, `--webrtc-ip-handling-policy`, and Mosaic sends `disable_non_proxied_udp` on every launch of a profile that has a proxy and the protection on, which is the default. It is Chrome's version of the strictest mode in RFC 8828, the IETF document on WebRTC address handling: if the browser's web traffic goes through a proxy, WebRTC media has to go through it too, and because the proxy does not carry UDP, UDP is off and media can only travel over TCP through the proxy.

We measured the switch on our own engine, one fresh browser data directory per row, no proxy in either:

```
no policy                  RTCPeerConnection present, 2 candidates
                           (mDNS host + server-reflexive, the real WAN address)
disable_non_proxied_udp    RTCPeerConnection present, 0 candidates

```

Zero candidates is the protection. Nothing about the computer's own address comes out, and a page sees what any browser on a network that blocks UDP shows. It is also the reason a call stalls at "connecting": with no candidates of its own, the browser has nothing to offer unless a relay gives it one.

Mosaic is a desktop app

Email yourself the link and open it on your computer.

How it works: Checked before every launch

## When a call can still connect

What is left is TURN over TCP, or over TLS, reached through the proxy. The browser opens a TCP connection through the proxy to the call service's TURN server, the relay allocates an address, and media is carried inside that connection. The other side of the call sees the relay, and the relay sees the proxy's exit rather than the computer's own address.

That only works when all of these hold:

1. The call service gives the browser a TURN server that accepts TCP or TLS. Some WebRTC apps configure UDP relays only, or none at all.
2. The proxy lets the browser connect to that relay's port. A proxy that only allows ports 80 and 443 needs the relay to be on 443.
3. The audio and video can tolerate the route. Media inside TCP stalls whenever a packet is lost, because TCP waits for the retransmission where UDP would have skipped it, and the path now runs through the proxy's exit as well as the relay. On a residential exit, which is somebody's home connection, that can mean choppy video even when the call connects.

We have not measured particular calling services through a proxied profile, so we will not name which ones pass. The checklist above is what decides it for any of them.

## What we do not say about it

Mosaic stops WebRTC from going around the proxy. We do not say it routes WebRTC through the proxy, because the engine does not carry UDP media over a proxy and cannot. Whether a call travels through the proxy at all depends on the service offering a TCP relay, which is outside the browser.

Removing WebRTC entirely would look tidier and be worse. A Chrome with no `RTCPeerConnection` is a browser that does not exist, while one that gathers nothing is every machine on a network that blocks UDP ([the measurements behind that choice](https://mosaicbrowser.com/blog/disabling-webrtc-is-a-tell)). And the address cannot be swapped for the proxy's either, since a server-reflexive address is whatever a STUN server saw the packet come from ([what "replace WebRTC IP with proxy IP" can mean](https://mosaicbrowser.com/blog/replace-webrtc-ip-with-proxy-ip)).

## If a profile really needs to make calls

There are two honest options. The first is to keep calls out of the proxied profile: a profile with no proxy does not get the policy, so WebRTC there behaves as stock Chrome does, from the address the site already sees.

The second is to turn the protection off for that one profile. In the profile editor, open the Privacy tab and switch off Block non-proxied UDP (WebRTC). Calls can then connect directly, and that is exactly what the switch was preventing: the people on the call, the service and any page that asks can see this computer's own address beside the proxy's exit. Do it only where that is acceptable.

Before blaming WebRTC at all, make sure the proxy itself answers and tunnels ([how to check a proxy before you use it](https://mosaicbrowser.com/guides/how-to-check-a-proxy-before-you-use-it)); a call cannot reach a relay through a proxy that is already failing. The flag names Chromium actually reads, and two it silently ignored, are in [two WebRTC flags that never existed](https://mosaicbrowser.com/blog/webrtc-flags-chromium-ignores).

## Questions

### Why does WebRTC not work with a proxy?

WebRTC carries audio and video over UDP, and Chrome's HTTP and SOCKS proxies carry TCP only. With non-proxied UDP blocked, the only route left is a TCP connection through the proxy to a TURN relay. If the call service offers no relay reachable that way, the call cannot connect.

### Does Mosaic route WebRTC through the proxy?

No, and we do not describe it that way. With a proxy attached, Mosaic launches the engine with --webrtc-ip-handling-policy=disable_non_proxied_udp, which stops WebRTC from going around the proxy. Whether a call then works depends on the service having a TURN relay the browser can reach over TCP.

### Can I turn the WebRTC protection off for one profile?

Yes. In the profile editor, Privacy tab, switch off Block non-proxied UDP (WebRTC). Calls can then connect directly, and the other side of the call and the service see this computer's own address instead of the proxy's exit.

### Do video calls work in a Mosaic profile with no proxy?

The policy is not sent when a profile has no proxy, unless its WebRTC setting is Gather nothing, so WebRTC behaves as it does in stock Chrome. There is no proxy to go around, and the address the call uses is the one the site already sees.

## Related

[A browser with no RTCPeerConnection doesn't exist](https://mosaicbrowser.com/blog/disabling-webrtc-is-a-tell): Deleting WebRTC leaves a Chrome no one runs. Zero candidates with the API still present is every machine on a UDP-blocked network, so that is what we ship.

[What "replace WebRTC IP with proxy IP" can actually mean](https://mosaicbrowser.com/blog/replace-webrtc-ip-with-proxy-ip): A WebRTC public address is what a STUN server saw a packet come from, so it cannot be swapped for the proxy's. What works is gathering no address outside it.

[Two WebRTC flags we used never existed in Chromium](https://mosaicbrowser.com/blog/webrtc-flags-chromium-ignores): Chrome has one WebRTC command line flag for leaks: --webrtc-ip-handling-policy. We also sent two that don't exist, and Chromium accepted both in silence.

[The .local address in your WebRTC test is Chrome working](https://mosaicbrowser.com/blog/webrtc-local-address-mdns): A random .local name in a WebRTC leak test is Chrome hiding your LAN address. We once switched it off, and the empty result was the louder tell.

[How to check a proxy before you use it](https://mosaicbrowser.com/guides/how-to-check-a-proxy-before-you-use-it): Check that a proxy answers and tunnels, where its exit really is, whether the address is residential or blocklisted, and that the browser stays behind it.



[WebRTC and IPv6: the candidate your proxy never carried](https://mosaicbrowser.com/blog/webrtc-ipv6-candidates): A WebRTC IPv6 leak is a candidate gathered over UDP that an IPv4 proxy never sees. Blocking non-proxied UDP closes it for both address families at once.



## Mosaic, on your own computer

The browser this blog is about: its own Chromium, fingerprints from real machines and a proxy exit per profile. Free holds 3 profiles. Pro is $49 a month for any number.

Mosaic is a desktop app

Email yourself the link and open it on your computer.
