Chrome does not use QUIC through the kinds of proxy people actually configure. With an HTTP, HTTPS or SOCKS5 proxy set, it reaches every site over TCP: it asks the proxy for a tunnel and speaks HTTP/2 or HTTP/1.1 inside it. So a site that would get HTTP/3 from a direct visit gets HTTP/2 from the same browser behind a proxy, and both the server and the page can see which one it was.
Why QUIC needs a path the proxy does not give
HTTP/3 runs on QUIC, and QUIC runs on UDP. A direct Chrome learns that a site speaks it from an Alt-Svc: h3=":443" header on an earlier TCP response (or from the site's DNS records), remembers that, and races a QUIC connection on the next visit. Both halves need UDP packets from the browser to the site.
An explicit proxy removes that path. Chromium's own proxy notes, net/docs/proxy.md in the source tree, describe each scheme. An HTTP proxy carries an https:// request as a CONNECT tunnel, and that tunnel is a TCP connection. An HTTPS proxy is the same with TLS to the proxy, and may use HTTP/2 to the proxy but not QUIC. Chrome's SOCKS5 support only proxies TCP-based requests and cannot relay UDP at all. Name resolution happens on the proxy's side for HTTP and SOCKS5 proxies alike, so the browser never even learns the site's address to send a UDP packet to.
Chromium also has code for proxies that themselves speak QUIC. That is a different thing from what a proxy seller hands you: residential and datacenter gateways are HTTP or SOCKS5 on a TCP port, which is why the port number in a proxy line often names its protocol.
Paste one gateway line. Every profile mints its own exitIn Mosaic: Provider minting. One gateway line, a separate exit for every 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.
How it works: Provider minting
Why people say QUIC bypasses the proxy
Search for this and the first results are network administrators complaining that QUIC goes around their proxy. They are describing a different setup. A transparent or intercepting proxy sits on the network, and the browser does not know it exists, so Chrome happily sends UDP to port 443 and the interceptor never sees it. Their fix is to block UDP 443 at the firewall, after which Chrome falls back to TCP.
With an explicit proxy, the one passed to Chrome as --proxy-server, Chrome knows, and web requests do not leave by any other road. We nearly got this wrong ourselves before launch. A profile's HTTPS appeared to skip the proxy on relaunch, the counter on our test proxy read zero, and "QUIC via a cached alt-svc" was one of four mechanisms we worked out in detail as the cause. It was none of them. We had reset the counter after the browser started, and a restored tab had already opened its tunnel during startup. Measured from before the browser existed, the host was in the proxy's log on every launch.
What a site sees: a proxied visit next to a direct one
The difference is real and readable. On a direct visit to a site that offers HTTP/3, the second page load will usually say h3. Through a proxy it says h2. The server sees it on the connection, and the page can read it itself:
performance.getEntriesByType('navigation')[0].nextHopProtocol
// "h3" direct, "h2" through the proxy
Does that mark a browser as proxied? Weakly at most. Plenty of real Chrome users never get HTTP/3, because office firewalls and some home routers drop UDP on 443, and Chrome then stays on TCP without saying anything. A site that wanted to know about a proxy has louder things to read, starting with the exit address and whose network it belongs to.
What matters more is that the TCP path carries fingerprints of its own, and they have to be Chrome's. The TLS handshake and the opening HTTP/2 frames are both read before any page exists. Tesseract Chrome, our engine, does not patch Chrome's networking code, and when we compared its HTTP/2 SETTINGS, header order and ALPN against Google Chrome they were the same (the Akamai HTTP/2 fingerprint, read field by field, with the probe). Why the handshake hash still changes between connections is in why Chrome's JA3 changes.
The other UDP: WebRTC
QUIC is not the only UDP a browser sends. WebRTC opens UDP connections for calls and data channels, and the SOCKS5 limit above applies to it too: there is no way through the proxy for it. Left alone, it goes direct and shows the real address.
So a Mosaic profile with a proxy attached starts with --webrtc-ip-handling-policy=disable_non_proxied_udp (the privacy.blockNonProxiedUdp setting, on by default). WebRTC cannot send UDP around the proxy. We do not claim it routes WebRTC through the proxy, because for UDP there is nothing to route it through. Mosaic also does not pass --disable-quic: a profile with no proxy browses from your own connection and uses HTTP/3 exactly as Chrome would there, and a profile behind a proxy is already on TCP. Each profile minted from a proxy provider gets its own session and exit, and that exit decides everything a site learns from the network.






