Web Development

WebTransport in Production: The Datagrams Were Easy, the Fallback Was the Whole Job

One connection carrying reliable streams and unreliable datagrams is a genuinely better shape for a live UI. Then you find out how many of your users sit behind a network that quietly drops UDP, and the interesting engineering turns out to be the path you hoped nobody would take.

KAKabir AnandLead Developer
9 min read
Editorial photograph of two parallel service tunnels beneath a street, one lit and carrying cabling, the other sealed off with a bolted steel plate

For eleven days, one of our client's four dispatch offices was running on the fallback transport and nobody knew. Not degraded, not broken — the board updated, vehicles moved, orders changed state. It was simply doing all of that over the WebSocket path we had built as a safety net and expected roughly nobody to use. We found it because a dispatcher mentioned in passing that the map felt "a bit sticky compared to Tuesdays," and Tuesdays were the days that office worked from a different building.

The rollout had gone well by every metric we were collecting. That was the problem. We were collecting metrics about the system, and we had no metric that answered the only question that mattered: which transport is this particular session actually on right now.

We have now shipped WebTransport in two production products and scoped a third. The honest summary is that the protocol work — streams, datagrams, deciding what rides on which — took about two days of real thinking. The other eight weeks went into the part where it does not connect, and into finding out who that happens to.

What changed is Safari, and that is the entire story

WebTransport is not new. Chrome shipped it in 2022, Firefox followed in 2023, and for three years it sat in the category of technology you could demo but not deploy, because a transport with a browser-shaped hole in it is not a transport. Safari 26.4 closed that hole in March as part of the Interop 2026 work, and the calculus changed in a single release. Not because the API improved — it did not — but because the fallback path stopped being the primary path for a quarter of your traffic.

What you get is one connection over HTTP/3 that carries two very different things at once. Reliable, ordered streams, as many as you want, with no head-of-line blocking between them — one slow message on stream A does not stall stream B, which is the failure mode that makes a single multiplexed WebSocket feel laggy under load. And unreliable datagrams, which are fire-and-forget, may arrive out of order, and may not arrive at all.

That second channel is the actual reason to do this. Everything else is a nicer version of something you already had.

The split takes an afternoon if you ask the right question

Teams get stuck on this because they frame it as a performance question — which channel is faster — and the answer is that neither is, particularly. The useful question is different and it takes about two minutes per message type: if this arrives four hundred milliseconds late, what should happen?

For a cursor position, a vehicle's last GPS ping, or a typing indicator, the answer is that nothing should happen, because the information is already wrong by the time it lands and a newer one is right behind it. Retransmitting it is worse than dropping it. For an order moving from assigned to collected, the answer is that it must land, in order, or two people are looking at different versions of reality. Once you ask it that way the classification writes itself.

SignalChannelWhy
Order and dispatch stateReliable streamEvery transition has to land, in order, or two offices disagree about a live job. Losing one is a correctness bug, not a visual glitch.
Vehicle positions, cursors, presenceDatagramA position that arrives late is simply wrong, and a newer one is already in flight. Dropping it is the correct behaviour.
Client-side telemetryDatagramWe sample it at the aggregator anyway. Paying for retransmission of data we then throw away is pure cost.
Auth, session join, acknowledgementsReliable streamControl plane. If one of these is lost the session ends up in a state neither side can name or recover from.

Reliable and unreliable are not quality tiers. They are a question about what should happen when a message arrives late, and for most of a live interface the honest answer is nothing at all.

The line that ended a two-week argument about which channel presence belonged on

One practical constraint that is easy to miss until it bites: a datagram is not a place to put an arbitrary payload. Keep it under roughly 1,200 bytes and it survives every path we tested; go over and behaviour varies by network in ways you will discover from a user rather than from a test. That ceiling is fine for positions and presence. It is not fine for the batched update someone will inevitably try to move onto datagrams in month three because it looked faster in a benchmark.

Flat vector illustration of two parallel lanes leaving a single gate, the upper lane carrying identical boxes evenly spaced in strict order, the lower lane carrying loose scattered tokens with several falling away
One connection, two contracts. The lower lane dropping things is not a failure — it is what you asked for.

UDP is not a guarantee, and your users are the ones who find out

WebTransport runs over QUIC, which runs over UDP. A meaningful share of the networks real people work on treat UDP on anything other than port 53 as something to be throttled, inspected, or dropped without comment. Corporate firewalls do it by policy. Some hotel and airport networks do it because whoever configured them did it once in 2016. A couple of mobile carriers do something stranger, where the connection establishes and then dies after a few minutes of idle.

None of this is exotic and all of it is invisible to you until you measure it. Here is what our first eight weeks across two products looked like, on a user base that skews toward office desktops — which is to say, the friendly case.

The second number is the one that reorganised our thinking. We had built the fallback as a legacy-browser concession, something that would shrink to nothing as Safari updates rolled out. It is not that. It is a network concession, and it does not shrink, because the networks are not going to be upgraded on anyone's roadmap. Two of those three corporate networks belong to the client's largest customers.

A transport that works for ninety-eight percent of sessions is not a transport. It is an optimisation with a support queue attached to it.

From the rollout note we sent the client after the first week of per-transport telemetry

Which means the WebSocket path is not scaffolding to be removed next year. It is a permanent second implementation that has to stay correct, stay tested, and carry the same semantics — and the cost of that should be in the estimate at the start, not discovered in month two. If a team is not willing to own two transports indefinitely, they should not be adopting this one yet.

Reconnection is yours, and so is the edge

WebSocket did not give you reconnection either, but everyone using it in production has been sitting on a library that did. WebTransport is early enough that most teams write against the raw API, notice that the connection-closed promise resolves cleanly, and ship. Then a laptop lid closes, or a train goes into a tunnel, and the session is simply over with no error anywhere — because from the API's point of view nothing went wrong.

  • Resume state, not the socket— reconnecting is the easy half. The hard half is that the client missed an unknown number of stream messages while it was away, so every reliable channel needs a sequence number and a “send me everything after N” path. Without it, reconnection silently produces a stale board that looks live.
  • Fall back on a timer, not on an error— a blocked UDP path often does not fail, it hangs. We give the connection attempt three seconds, then open the WebSocket in parallel and take whichever is ready first. Waiting for a definitive failure means waiting past the point the user has already refreshed.
  • Re-probe, but rarely— a session that fell back at 09:00 because of a flaky moment should not be stuck on the slower path until lunch. We retry the primary transport once, quietly, after fifteen minutes, and never again in that session. More often than that and you are generating churn to chase a difference nobody can perceive.

The infrastructure side surprised us more than the client side did. HTTP/3 has to be terminated somewhere that speaks it, and a lot of perfectly modern production edges do not. Two of the three environments we touched had a load balancer that was TCP and HTTP/2 only, so the work was not "enable a flag" but "introduce a new ingress path, with its own certificates, its own health checks, and its own on-call runbook, next to the one that already works." Budget for that conversation with whoever owns the edge before you promise a date.

Isometric illustration of a building with two entrances, the direct one closed off by a lowered barrier while a stream of small carts takes a longer external ramp around the side
When the direct path is blocked the traffic does not stop, it takes the long way. The failure is that nothing tells you it happened.

If you cannot see the transport, you cannot see the problem

This is what cost us eleven days. Every dashboard we had aggregated across all sessions, so a whole office quietly sitting on the slower path moved the p95 by an amount that looked like normal variance. The system was healthy in every chart. The people using it were not.

The fix is unglamorous and should be built before the feature ships, not after: the transport in use becomes a dimension on every client-side metric you already collect, and the fallback rate becomes a first-class number with an alert on it. Not the fallback count, the rate, split by network or by tenant — a count going up is indistinguishable from growth, which is exactly why nobody looks at it twice.

We also log the reason for falling back, and that has been worth more than the rate itself. "Connection attempt timed out" and "connection established then closed after ninety seconds" are both counted as fallback and they are completely different problems — the first is a firewall, the second was, in our case, an idle timeout on an intermediary that nobody at the client knew was in the path.

What we would tell a team starting this on Monday

  • Sort messages by what should happen if they arrive late, not by how fast they need to be. That single question does the whole reliable-versus-unreliable design for you.
  • Treat the WebSocket path as a permanent second implementation with the same semantics and the same tests, not as scaffolding you will delete once browser support catches up. It is a network concession, and networks do not update.
  • Instrument the transport as a dimension on every client metric before you ship, or you will average a broken office into a healthy-looking chart.
  • Fall back on a timer, not on an error. A blocked UDP path frequently hangs rather than failing, and the user refreshes long before the connection gives up.
  • Check who terminates HTTP/3 at your edge in week one. In two of three environments this was a new ingress with its own certificates, not a configuration change.
  • Keep datagram payloads under about 1,200 bytes and put a comment on it, because the person who later batches an update onto that channel will not find the limit in testing.

Was it worth doing

For the live-operations board, yes, and for a specific reason that has little to do with latency numbers. Moving vehicle positions onto datagrams meant a slow or dropped position update stopped being able to delay an order state change, because they no longer share a delivery guarantee or a queue. The board got less laggy under load, but more importantly it got harder to make wrong. That is a correctness win wearing a performance costume.

For the second product — a document editor where essentially every message has to land — it was not worth it, and we said so before building anything. Everything in that product needs a reliable ordered channel. WebTransport would have given it a slightly better multiplexing story and a second transport to maintain forever. That trade is not close.

So the test we use now is narrow on purpose: do you have a meaningful volume of messages that are genuinely disposable? If yes, this is a real improvement and the datagram channel is the thing you are buying. If no, you are taking on a permanent dual-transport maintenance cost to get a nicer version of what WebSocket already does for you, and a boring transport that everyone on the team understands is worth more than an interesting one that two people do.

Keep Reading

More from the blog

Track Record

The Engineering Partner You Can Build On

Reliable software takes an experienced team that owns delivery end to end. Here’s the track record behind ours.

Book a Free Consultation
01

11+

Years Building Custom Software

02

320+

Projects Delivered Across Web, Mobile & AI

03

85%

Repeat Client Rate

04

12+

Countries Served Worldwide

Trusted by startups and enterprises worldwide

Work With Us

Let’s create
with purpose

Share your goals, timeline, and challenges — we’ll respond with clarity and next steps.

Ambitious ideas deserve thoughtful execution. Start the conversation and let’s define what success looks like.

Team

Acetrum

Est. 2015

4.9/5

Trusted by
top brands

Services interested in:

By submitting, I confirm I’ve read and agree with Privacy and Cookie Policies.

Newsletter

Signals worth
paying attention

No recycled headlines — just the patterns we’re seeing across real client work, distilled into one read a month.

A curated digest of practical thinking and real-world brand perspectives monthly.

No spam. Unsubscribe anytime.