On office Wi-Fi the call connects in under a second. On 4G it never connects at all. Move back to Wi-Fi and it works again. Nothing in your code changed, and the behaviour is completely reproducible once you know what to look for.
This is NAT traversal, and the reason it feels random is that the failure is not a property of either endpoint. It is a property of the pair.
What NAT is actually doing
Your device has a private address that means nothing on the public internet. A router sits between you and the world, rewriting the source address of outbound packets to its own public address and recording the substitution in a mapping table so that replies can be sent back to you.
For anything you initiate this is invisible and works perfectly. You send a request, the mapping exists, the response comes back through it.
The problem is anything you do not initiate. An inbound packet arriving at the router with no matching table entry has nowhere to go and gets dropped. A peer-to-peer video call is precisely that case — two devices, both behind their own routers, each needing to send to the other without either having asked first.
The workaround is to make both sides initiate at the same moment, so that each one's outbound packet creates the mapping the other's inbound packet needs. That is hole punching, and whether it works depends entirely on how predictable those mappings are.
Mapping behaviour is the variable that matters
You will see NAT described as full cone, restricted cone, port-restricted cone and symmetric. That taxonomy comes from an early STUN specification and is worth knowing because people still use it, but it is not how the standards describe NAT any more.
RFC 4787 abandoned it explicitly, on the grounds that it "has been the source of much confusion, as it has proven inadequate at describing real-life NAT behavior." It separates two things the cone names conflated: how the external port is assigned, and who is permitted to send back through it.
For hole punching, the first is what decides everything. Three behaviours:
- Endpoint-independent mapping — one external port per internal socket, regardless of where you are sending. The mapping you discover talking to one server is the mapping everyone else sees.
- Address-dependent mapping — a new mapping per destination address.
- Address-and-port-dependent mapping — a new mapping for every destination address and port combination. This is what people mean by symmetric NAT.
Here is the part worth remembering. RFC 4787's first requirement is unambiguous: "A NAT MUST have an 'Endpoint-Independent Mapping' behavior." Address-and-port-dependent mapping violates a MUST in the standard — and it is deployed at enormous scale anyway.
Why hole punching works, and exactly when it cannot
The mechanism is simple once the mapping question is clear. Each device contacts a STUN server, which replies with the public address and port it observed. That is the device's mapping as seen from outside. The two peers exchange these discovered addresses through your signalling channel, then both send packets to the other's address simultaneously. Each outbound packet opens the mapping that the incoming one needs. The call connects.
This requires the discovered mapping to be the same mapping used towards the peer. With endpoint-independent mapping it is, and hole punching works.
With address-and-port-dependent mapping it is not. The mapping discovered while talking to the STUN server applies only to the STUN server. Sending to the peer creates a different mapping on a different port, which the peer does not know and cannot guess. The address exchanged through signalling is already obsolete by the time it is used.
One side behaving this way is usually survivable. The well-behaved side has a stable, predictable address, so the difficult side can reach it — and once that packet arrives, the well-behaved side simply replies to whatever source address it actually came from, learning the real mapping by observation.
Both sides behaving this way is a deadlock. Neither can learn the other's mapping without receiving a packet, and neither can send a packet to a correct destination. No amount of retrying fixes it.
That last row is the whole article. It is not probabilistic and it is not a tuning problem: that combination cannot establish a direct path, and the only fix is to stop trying and use a relay.
Why mobile networks are where you meet it
There are not enough IPv4 addresses for the number of connected devices, and mobile carriers ran out first. Their solution is carrier-grade NAT — a second layer of translation, above the one in your router, sharing a small pool of public addresses across a very large number of subscribers.
At that scale, address-and-port-dependent mapping is the operationally sensible choice. It maximises how many subscribers fit behind each public address and it avoids mapping collisions. The standard says MUST; the address arithmetic says otherwise, and the address arithmetic wins.
Which produces exactly the symptom this post opened with. Home and office routers usually implement endpoint-independent mapping, so Wi-Fi to Wi-Fi hole punches cleanly. A mobile device behind CGNAT calling a Wi-Fi peer usually still works, because the Wi-Fi side is predictable enough to rescue it. Two mobile devices, both behind carrier-grade NAT, is the bottom row of the table — and it fails every time.
It is also why connectivity planning matters more for anything mobile-first. When vehicles stream over exactly these networks, relay is not an edge case to handle — it is the normal path, and the architecture should assume it rather than hope.
How ICE handles all of this, and what it costs
You do not implement any of this yourself. ICE gathers candidates and probes the pairings — local addresses, STUN-discovered public addresses, and relayed addresses on a TURN server — then tests every plausible combination and selects the best one that works. Hole punching is attempted automatically, and relay is used only when nothing else succeeds.
So the framework handles the logic. What it cannot do is invent a path that does not exist, which is why TURN is not optional infrastructure for any application with mobile users.
And a relay is a real cost, not a fallback that is free because it is rare. Media passes through your infrastructure and leaves it again, so what running that relay costs should budget it as a line item rather than appear as a surprise on the first mobile-heavy month.
One thing this post is deliberately not about: corporate firewalls. Those fail for different reasons — blocked UDP, closed ports, proxies, egress allowlists — and corporate networks break it a different way. NAT behaviour and network policy are separate problems that happen to share a fix.
Does IPv6 solve this?
Partly, and less than people hope.
IPv6 removes the reason NAT exists. There are enough addresses for every device to have its own, so no translation layer is required and the mapping problem disappears entirely. Where both endpoints have working IPv6, connectivity is genuinely simpler.
Three things stop this from being the answer. Firewalls remain — a globally routable address is not a reachable one, and stateful filtering still blocks unsolicited inbound traffic, so you still need the same connectivity checks. Coverage is uneven, so any real deployment is dual-stack and has to work when one side has no usable IPv6 at all. And ICE handles both address families simultaneously, which means IPv6 becomes another set of candidates to try rather than a replacement for the process.
The practical read: IPv6 improves your odds of a direct path and removes none of the machinery. Plan for TURN regardless.
Build, Buy, or Deploy
Build on open source
The browser implements ICE, STUN and TURN client behaviour for you — none of that is yours to write. What is yours is the relay: deploying coturn, placing it near users, issuing credentials, and sizing for a relay rate that depends on your users' networks rather than your traffic. For a mobile-first product that rate is substantially higher than the general figures suggest.
Buy a cloud video API
Per-minute providers include relay infrastructure, and for connectivity specifically this is one of their strongest arguments — a large distributed relay footprint is genuinely hard to replicate. The limit is visibility: you generally cannot see what share of your sessions are relayed, which is the number that tells you whether your users are where you think they are.
Deploy a commercial platform
The middle path runs a commercial platform on infrastructure you choose. Samvyo is one such option: based on SFU architecture, shipping embeddable SDKs, with the media path, TURN and recording kept on infrastructure you control. Relay placement is the relevant part — for mobile users the physical distance to the relay is a direct latency cost, and choosing where it sits is choosing how the fallback path performs. It is resilient by design rather than something you assemble.
Where it does not fit: if every user is on a managed network you administer, you can often arrange direct connectivity and skip the relay question. And for a low-volume consumer product, a per-minute API bundles relay and costs less.
The Bottom Line
NAT traversal fails on a pairing, not on an endpoint, which is why it looks random until you know what to measure. When both sides assign a new mapping per destination — the behaviour RFC 4787 forbids and carrier-grade NAT uses anyway — no direct path exists and a relay is mandatory.
Wi-Fi to Wi-Fi usually hole punches. Mobile to Wi-Fi usually survives, because the predictable side rescues the unpredictable one. Mobile to mobile across carrier NAT is the combination that always relays. Budget TURN accordingly, and expect a higher relay rate than the averages if your users are mobile.
What's Next
For where NAT traversal sits in the wider pipeline, the full chain between two video tabs walks all eight stages. For the other half of the connectivity problem — corporate policy rather than NAT behaviour — corporate networks break it a different way covers blocked UDP, proxies and the fallback ladder.
Frequently Asked Questions
Why does my WebRTC call work on Wi-Fi but fail on 4G?
Because mobile carriers use carrier-grade NAT, which typically assigns a different external port for every destination. That makes the address discovered via STUN useless for reaching your peer, so hole punching cannot work. Home and office routers usually keep one stable mapping per socket, which is why Wi-Fi connects cleanly.
What is NAT traversal in WebRTC?
The process of establishing a media path between two devices that both sit behind address translation. ICE gathers candidate addresses — local, STUN-discovered public, and TURN-relayed — then tests every plausible pairing and uses the best one that works. Hole punching is attempted first; a relay is used only when nothing else connects.
What is symmetric NAT and why does it break WebRTC?
It is the informal name for address-and-port-dependent mapping: the router creates a new external port for each destination. The mapping discovered while contacting a STUN server therefore does not apply to your peer, so the address you exchange is already wrong. RFC 4787 actually requires endpoint-independent mapping, but carrier-grade NAT does not comply.
Can two devices both behind symmetric NAT ever connect directly?
No. Neither side can learn the other's mapping without first receiving a packet, and neither can send a packet to a correct destination. That combination always requires a relay, and it is not a tuning problem — it is structural.
Does IPv6 fix NAT traversal?
It helps and does not eliminate the work. IPv6 removes the need for address translation, so the mapping problem disappears where both sides have working IPv6. Firewalls still block unsolicited inbound traffic, coverage is uneven so deployments stay dual-stack, and ICE treats IPv6 as additional candidates rather than a replacement. Plan for TURN regardless.
Do I need a TURN server if I only support mobile users?
Especially then. Mobile-to-mobile calls across carrier-grade NAT are the pairing that always requires a relay, so for a mobile-first product relay is the normal path rather than an exception. Expect a materially higher relay rate — and relay bill — than general figures suggest.
Is NAT traversal the same problem as corporate firewalls blocking WebRTC?
Related but distinct. NAT traversal is about address mapping and whether a direct path can be discovered. Corporate firewall failures are about policy — blocked UDP, closed ports, forced proxies, egress allowlists. They share a fix in TURN, but they fail for different reasons and need different diagnosis.