Repository navigation
T3 Connect: let users force the managed cloudflared to HTTP/2 — on QUIC-blocking corporate networks the tunnel stays down because cloudflared won't fall back #16344
Copy link
Copy link
Open
Labels
bugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
Description
Activity
Note
Grok responding on behalf of Julius.
Triage
Thanks for the measurements and the detailed cloudflared walkthrough, @mats16! This lines up with what we see in the code, and it's separate from the nearby tunnel issues.
What I found
- The managed connector starts with a fixed argv and no transport flag.
CloudManagedEndpointRuntimeinapps/server/src/cloud/ManagedEndpointRuntime.tsspawnscloudflared tunnel --no-autoupdate --loglevel info --output default runand adds onlyTUNNEL_TOKENon top ofprocess.env. There's no--protocol, server flag, or setting. The pinned binary is cloudflared2026.5.2(CLOUDFLARED_VERSIONinpackages/shared/src/relayClient.ts), andrunningmeans the child is alive, not that a tunnel connection registered. TUNNEL_TRANSPORT_PROTOCOLis cloudflared's own env var for its hidden--protocolflag. Because the spawn spreadsprocess.env, yourlaunchctl setenvworkaround reaches the child after a relaunch. That works through inheritance rather than a supported setting.- On
autowith a tunnel token, cloudflared2026.5.2starts on QUIC and treats HTTP/2 as a fallback. The three behaviors you described are all in that tag:tunnelstate.ConnTracker.HasConnectedWithmatches on protocol only, and a disconnect leaves the protocol record in place, so one QUIC registration suppresses HTTP/2 fallback for the rest of the process.- A fresh process can't fall back until
IpAddrFallbackhas rotated edge addressesMaxEdgeAddrRetriestimes (default 8). With a 5s QUIC handshake idle timeout and backoff capped around 30s, that's a multi-minute stall. Startup connectivity pre-checks are diagnostic only. - A successful registration calls
protocolFallback.reset(), which clearsinFallbackbut leaves the selector on QUIC, so the next retry goes back to QUIC ("registered http2, then quic again").
superviseConnectoronly restarts the child after it exits. A zero-connection watchdog that respawns with the sameautoargs (like the one proposed on [Bug]: T3 Connect stays down after a network change: cloudflared keeps running with zero connections and is never restarted #16258) would reset the QUIC memory and start on QUIC again, and could restart before the edge-address rotations finish.- Related but distinct: [Bug]: T3 Connect stays down after a network change: cloudflared keeps running with zero connections and is never restarted #16258 (stuck at zero connections with no restart), T3 Connect: new connections fail after the host joins a VPN because the managed cloudflared keeps a stale QUIC MTU #15897 (stale QUIC MTU;
/readystays 200), [Bug]: T3 Connect treats a spawned but unreachable tunnel as 'running' #7447 (status saysrunningbefore the first registration).
Likely fix area
Some options:
- Pass a transport choice (
autodefault,http2,quic) through to the spawn as--protocol, reachable from botht3 serveand the desktop app. Since the Connect origin is loopback HTTP rather than Cloudflare private routing, the ICMP/UDP limitation cloudflared prints for HTTP/2 shouldn't matter here. - If a zero-connection watchdog lands, let the respawn switch to
--protocol http2after QUIC fails to register, since a plainautorestart won't stick on these networks.
A maintainer will decide on the fix direction.
- The managed connector starts with a fixed argv and no transport flag.
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 6, 2026
Metadata
Metadata
Assignees
Labels
bugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
Before submitting
Area
apps/server
Summary
On networks that drop QUIC (UDP 7844) but allow TCP 7844, T3 Connect goes down for long periods. The managed
cloudflaredkeeps dialing QUIC instead of using HTTP/2, which works on the same networks. This is common on corporate networks: full-tunnel VPNs that fall back from IPsec to an SSL/TLS (TCP 443) tunnel often stop passing QUIC, and some office and tethered networks behave the same way.T3 always spawns
cloudflared tunnel --no-autoupdate --loglevel info --output default runwith the default--protocol auto, and it offers no way to choose the transport. A user who knows the network blocks QUIC can't do anything about it. A watchdog that restartscloudflared(as proposed in #16258) helps less than you'd expect here, for the reasons below.Why cloudflared doesn't recover on its own (cloudflared 2026.5.2)
supervisor/tunnel.go,Serve()returns before choosing a fallback whene.tracker.HasConnectedWith(e.config.ProtocolSelector.Current())is true. Intunnelstate/conntracker.go,HasConnectedWithonly checksci.Protocol == protocoland ignoresIsConnected. Disconnect events only setIsConnected = false, so the protocol record stays. A connector that used QUIC once, for example at home, never falls back to HTTP/2 after the laptop moves to a QUIC-blocking network. It sits atreadyConnections: 0until it is restarted.EdgeQuicDialError,IpAddrFallback.ShouldGetNewAddressonly reports a fallback-worthy connectivity error aftermaxRetriesedge-address rotations per connection, and the backoff between attempts grows exponentially. In our logs the retry gaps grew from about 20 s to 2 min to 4.5 min. A fresh connector that had never connected over QUIC took about 18 minutes to fall back.http2at startup, then registered overquicagain on a later reconnect within the same process. It then hit (1) on the next QUIC-blocking network.So restarting
cloudflaredresets (1), but on a QUIC-blocking network the new process still starts with QUIC and needs many minutes to fall back.Steps to reproduce
cloudflaredregisters 4 connections withprotocol=quic.curl -s http://127.0.0.1:<metrics-port>/ready. It returns{"status":503,"readyConnections":0,...}and stays there. The log shows repeatedfailed to dial to edge with quic: timeout: no recent network activityorhandshake did not complete in time.Remote environment endpoint https://<relay-host>/oauth/token timed out after 10000msor a similar error.Expected behavior
auto(default),http2, orquic, passed tocloudflaredas--protocol. An equivalent flag or env var fort3 servewould also help headless hosts.--protocol http2. This would fit the watchdog proposed in [Bug]: T3 Connect stays down after a network change: cloudflared keeps running with zero connections and is never restarted #16258. A restart that keepsautomay not help on these networks.Actual behavior
What we measured on the same host (macOS, Apple silicon):
/oauth/token0x0a0a0a0a). TCP 7844 was checked withnc -z.quic_client_total_connectionsclimbing (45 → 115) withquic_client_latest_rtt 0and no received bytes.protocol=http2within a minute on the same office network./.well-known/t3/environmentthrough the relay hostname answered200in about 0.1 s.Impact
Major degradation or frequent failure
Version or commit
Desktop 0.0.46-nightly.20261005.2702 (macOS "T3 Code (Nightly)")
Environment
macOS 26.6.2 (Apple silicon), managed cloudflared 2026.5.2, iOS app (build 105), corporate full-tunnel VPN with IPsec → SSL/TLS fallback
Logs or stack traces
Workaround
T3 spawns
cloudflaredwithenv: { ...process.env, TUNNEL_TOKEN }, socloudflared's own env var for--protocolis inherited:launchctl setenv TUNNEL_TRANSPORT_PROTOCOL http2 # then quit T3 Code and relaunch it from the Dock or FinderThis is undocumented, applies to every app launched afterwards, and is lost on reboot or logout. Turning the VPN off also works, but that's often not an option on a work machine.
Related
--protocol http2" as one possible direction.runningbefore it registers.