# Is disabling WebRTC suspicious? What a page actually sees

> 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.

Source: https://mosaicbrowser.com/blog/disabling-webrtc-is-a-tell

## A browser with no RTCPeerConnection doesn't exist

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.

![0: Candidates gathered, with the API still there, beside a screenshot: a Mosaic leak test: every surface a site reads, checked from inside the profile page](https://mosaicbrowser.com/covers/disabling-webrtc-is-a-tell.webp)

Disabling WebRTC is suspicious if it means deleting the API. Every Chrome people actually run defines `RTCPeerConnection`, so a page that looks for it and finds nothing has learned that this browser was modified, which is more than a leak would have told it. Keeping the API and letting it gather zero candidates is different: that is what any machine on a network that blocks UDP reports. We found this out by measuring our own "disabled" option, which turned out to do neither.

## What our "disabled" option was doing

Mosaic's profile editor has a WebRTC field with three choices. One of them used to be labelled "No RTCPeerConnection at all". The deletion behind that label lived in an injected script. On our own engine, Tesseract Chrome, the injection is switched off wholesale, because fingerprint values there are changed in C++ and never in page JavaScript. So the label promised a deletion that never ran.

We measured it through the leak test, on our engine, with the profile set to disabled:

```
webrtc: 'disabled'   2 candidates: an mDNS host candidate and
                     srflx carrying the host's real public address
leak test            fail: "set to disabled but still produced 2 candidates"

```

The leak test had been right all along. Nobody had run it on that setting.

It was worse than inert. Choosing disabled also turned off the engine's own handling of the local address for WebRTC. So the most protective-sounding choice in the list was the least protective one, and it was the one a careful person would pick.

Mosaic is a desktop app

Email yourself the link and open it on your computer.

[How it works: Machines that exist](https://mosaicbrowser.com/features/real-fingerprints)

## Two of the three flags were not switches

While we were there, we checked every WebRTC flag Mosaic sent against the binaries themselves, with `strings`, on our engine, a fork we compared with, and the machine's own Google Chrome:

```
webrtc-ip-handling-policy          ours 1   fork 2   stock 2
force-webrtc-ip-handling-policy    ours 0   fork 0   stock 0
webrtc-ip                          ours 0   fork 0   stock 0

```

Chromium accepts a switch it does not know without a word, so both of the missing ones had been passed at every launch and done nothing. The second mattered most. `--webrtc-ip` was the whole implementation of the editor's "Replace with proxy IP". Nothing replaced anything, on any engine, for as long as that option existed.

Nothing replaces it now either, because there is nothing for it to do. A server-reflexive candidate is whatever address the STUN packet left from. What protects a proxied profile is that no candidate is gathered around the proxy in the first place.

## Is disabling WebRTC suspicious? Only if the constructor goes

The one real flag was enough. We ran the same probe in a fresh profile folder per row, no proxy, on our engine and on Google Chrome:

```
                            ours                 Google Chrome
no policy                   RTC present, 2       RTC present, 2
disable_non_proxied_udp     RTC present, 0       RTC present, 0

```

Both browsers agree, which is the point. With the policy set, `RTCPeerConnection` is still a function a page can call, a peer connection can still be built, and gathering simply finishes with nothing. A page cannot tell that apart from an office network, a hotel or a mobile carrier that drops UDP. A missing constructor has no such crowd to hide in.

So "disabled" now means the policy switch, sent on every engine. The injected deletion is gone. The option is labelled "Gather nothing", with the hint "WebRTC stays present and produces no candidates", and on the leak test it went from 2 candidates and a fail to 0 and a pass. Any profile with a proxy attached gets the same policy by default, so no candidate escapes around the proxy.

We also took out something the old branch did quietly: it refused `getUserMedia`, the camera and microphone call. That belongs to a different setting, and one switch silently deciding another is how settings stop meaning what they say.

## The leak report erred in the flattering direction

The leak test is the one screen people trust instead of measuring, so a wrong line there costs more than a missing one. It had three, and all of them flattered the profile:

- With no proxy attached, it called the machine's own public address "the proxy exit".
- It reported "WebRTC exposed 0.0.0.0" for a candidate carrying `raddr 0.0.0.0`, which is what Chrome writes when it withholds the local address. The protection working was reported as a leak.
- It told an unproxied profile to "run a proxy check first", when there was no proxy to check.

The cause of the first and third was one value doing two jobs: an empty proxy address meant both "no proxy" and "proxy never checked". The report now gets those as separate facts, and a build check covers each of the three, proved by putting the fault back and watching the right assertion fail.

## Reading your own result

A `.local` host candidate is Chrome hiding your private address, not a leak; [the mDNS post](https://mosaicbrowser.com/blog/webrtc-local-address-mdns) explains why we leave it on. A server-reflexive candidate that is not your proxy's exit is the real leak. No candidates with `RTCPeerConnection` defined is the right shape on a proxied profile. And a test that finds no `RTCPeerConnection` at all is telling you about the browser, not the network.

WebRTC is one surface of a [browser fingerprint](https://mosaicbrowser.com/glossary/browser-fingerprint), and the rule behind this fix is the one behind [how Mosaic draws real fingerprints](https://mosaicbrowser.com/features/real-fingerprints): the right answer is the one a real Chrome would give. The proxy checks worth running before any of this are in [how to check a proxy before you use it](https://mosaicbrowser.com/guides/how-to-check-a-proxy-before-you-use-it).

## Questions

### Is disabling WebRTC suspicious?

Removing it is. Every current Chrome defines RTCPeerConnection, so a page that finds it missing is looking at a browser somebody changed. Keeping the API and letting it gather no candidates looks like an ordinary machine on a network that blocks UDP.

### What does Chrome's disable_non_proxied_udp policy do?

It stops WebRTC from sending UDP that does not go through the proxy. With a proxy set, STUN cannot reach the internet around it, so no candidate carrying your real public address is gathered. RTCPeerConnection stays defined.

### Do the flags --webrtc-ip and --force-webrtc-ip-handling-policy work?

Neither exists in Chrome. We checked the binaries with strings: only webrtc-ip-handling-policy is there. Chromium accepts an unknown switch without a word, so a flag that does nothing looks exactly like one that works.

### How do I check whether WebRTC leaks in a profile?

Run a WebRTC leak test inside the profile with its real proxy attached. A server-reflexive candidate that is not the proxy's exit is a leak. No candidates with RTCPeerConnection present is fine on a proxied profile.

## Related

[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.

[Browser fingerprint: what a site can read about your machine](https://mosaicbrowser.com/glossary/browser-fingerprint): A browser fingerprint is the set of values a site reads about your browser and computer, which together recognize you without cookies. What goes into one.

[Machines that exist: fingerprints drawn from real hardware](https://mosaicbrowser.com/features/real-fingerprints): Mosaic draws each profile's GPU, screen, CPU, memory and fonts from one real device, so the parts of a fingerprint agree with each other and with the host.

[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.









## 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.
