Why architecture matters here
NAT traversal matters because it's what makes peer-to-peer connectivity possible on the real internet, where nearly everyone is behind NAT -- foundational to WebRTC, VoIP, gaming, and P2P. Peer-to-peer connections (direct between users -- for video calls, gaming, file transfer -- avoiding a central server relaying everything) are valuable (lower latency -- direct path; lower server cost -- no relaying; scalability). But the reality of the internet is that nearly every user is behind NAT (home routers, carrier-grade NAT, corporate networks) -- so peers have no directly-reachable public address, and can't just connect. NAT traversal (STUN, hole punching, TURN, ICE) is what establishes the connection despite the NATs -- making peer-to-peer possible on the real internet. This is foundational to WebRTC (the browser P2P standard -- video calls, data channels), VoIP (voice over IP -- peer connections), online gaming (P2P game connections), and P2P applications. For anyone building real-time or peer-to-peer communication, understanding NAT traversal (how peers behind NATs connect) is essential, and it's the unavoidable complexity of P2P on the NAT-riddled internet.
The STUN-plus-hole-punching insight is the clever core, and it's how direct connection is achieved through NATs. The problem: a peer behind NAT has no reachable public address. STUN solves the first part (discovery): the peer asks a STUN server 'what address do you see me from?' -- and the STUN server replies with the public address:port the NAT presents (the server-reflexive address) -- so the peer learns its public address (as seen from outside). But knowing the address isn't enough -- the NAT won't forward inbound packets unless it has state for the connection. Hole punching solves this: the two peers (having exchanged their discovered public addresses via signaling) simultaneously send packets to each other's public addresses. When peer A sends to peer B's public address, A's NAT creates outbound state (and the packet may be dropped by B's NAT initially) -- but crucially, A's NAT now expects a reply from B (it has state for the A->B connection). When B simultaneously sends to A, B's NAT creates its state, and B's packet arrives at A's NAT -- which now has state (from A's outbound) allowing it in. So both NATs, having seen their peer's outbound packet, now allow the other's packets -- a hole is punched through both NATs, and a direct connection is established. This -- STUN discovering the public address, hole punching getting both NATs to open (via simultaneous outbound packets creating the necessary state) -- is the clever core of direct NAT traversal, and understanding it is understanding how peers behind NATs connect directly.
And the TURN-fallback-plus-ICE-orchestration reality is what makes NAT traversal robust, since direct connection doesn't always work. Hole punching works for many NAT types, but not all -- symmetric NATs (which assign a different public port for each destination) defeat it (the address discovered via STUN, for the STUN server's destination, differs from the port the NAT uses for the peer -- so the hole-punching packets don't match). When direct connection fails (symmetric NATs, restrictive firewalls), there's a fallback: TURN -- a relay server that both peers connect to (each peer connects outbound to the TURN server -- which all NATs allow, since it's an outbound connection), and the TURN server relays traffic between them. TURN always works (both peers can reach it outbound) but at a cost (all the traffic is relayed through the TURN server -- added latency and server cost -- so it's a fallback, not the preferred path). ICE orchestrates finding the best path: it gathers candidates (host -- local addresses; server-reflexive -- STUN-discovered public addresses; relay -- TURN addresses), and runs connectivity checks (testing which candidate pairs can actually connect) -- selecting the best working path (preferring direct -- host or server-reflexive -- over relayed -- TURN -- for lower latency and cost). So ICE tries direct first (hole punching via STUN candidates) and falls back to TURN (relay) if direct fails -- robustly establishing a connection (direct when possible, relayed when necessary). This -- TURN as the always-works relay fallback, ICE orchestrating the candidate gathering and connectivity checks to find the best path (direct preferred, relay fallback) -- is what makes NAT traversal robust (always establishing a connection, optimally), and understanding it is understanding how NAT traversal handles the cases where direct connection fails.
The architecture: every piece explained
Top row: the problem and the techniques. The problem: NAT hides private addresses (peers behind NAT have no directly-reachable public address -- the NAT only forwards inbound traffic for connections it has state for) -- so peers can't just connect. STUN: discovering the public address (a peer asks a STUN server 'what address do you see me from?' -- learning the public address:port the NAT presents -- the server-reflexive address). Hole punching: the peers simultaneously open to each other's public addresses -- each NAT, seeing its peer's outbound packet, creates state allowing the other's packets in -- punching a direct path through both NATs. TURN: the relay fallback (both peers connect outbound to a TURN server, which relays between them -- a guaranteed path when direct fails, at the cost of relaying all traffic).
Middle row: types and orchestration. NAT types: cone NATs (consistent public mapping -- hole-punchable) vs symmetric NATs (different public port per destination -- defeating simple hole punching, needing TURN) -- the NAT type determining whether direct works. ICE: gathering and prioritizing candidates (orchestrating the traversal -- gathering the candidate addresses and testing them to find a working path). Candidates: the possible addresses -- host (local addresses), server-reflexive/srflx (STUN-discovered public addresses), relay (TURN addresses) -- the candidates ICE tests. Connectivity checks: testing candidate pairs to find a working path (which pairs can actually connect) -- selecting the best (direct preferred over relay).
Bottom rows: signaling and usage. Signaling: exchanging candidates out-of-band (the peers exchange their candidates via a signaling channel -- a separate channel, e.g., a WebSocket via a server -- since they can't yet connect directly) -- so each peer knows the other's candidates to test. Where used: WebRTC (the browser P2P standard -- using ICE/STUN/TURN for its connections), P2P applications (file sharing, etc.), VoIP (voice -- peer connections) -- the applications of NAT traversal. The ops strip: TURN capacity (provisioning TURN relay capacity -- since TURN relays all the traffic for the connections that fall back to it, it needs sufficient bandwidth/capacity -- a real cost for the fraction of connections needing relay), NAT diversity (handling the diversity of NAT types -- cone, symmetric, restrictive -- ensuring traversal works across them, with TURN for the hard cases), and fallback (ensuring the TURN fallback works -- so connections always establish, even when direct fails -- the reliability).
End-to-end flow
Trace ICE establishing a WebRTC connection. Two peers (A and B, both behind NATs) want a direct connection. Each gathers candidates: host candidates (their local addresses), server-reflexive candidates (their public addresses, discovered via STUN -- asking a STUN server), and relay candidates (a TURN server's address, as a fallback). They exchange their candidates via signaling (an out-of-band channel -- a signaling server relaying the candidate lists). Then ICE runs connectivity checks: testing candidate pairs (A's candidates vs B's) to find a working path. It tries direct first: the server-reflexive candidates (the STUN-discovered public addresses) with hole punching -- A and B simultaneously send to each other's public addresses, punching holes through their NATs. If their NATs are cone-type (hole-punchable), the direct connection succeeds (a connectivity check passes for the direct pair) -- and ICE selects it (the direct path -- low latency, no relay). The peers now have a direct P2P connection (established through the NATs via STUN and hole punching, orchestrated by ICE). The ICE process (gather candidates, exchange via signaling, connectivity checks preferring direct) established the direct connection.
The symmetric-NAT and TURN-fallback vignettes show the fallback. A symmetric-NAT case: peer B is behind a symmetric NAT (assigning a different public port per destination). So B's server-reflexive candidate (the public address STUN discovered, for the STUN server's destination) doesn't match the port B's NAT uses for peer A -- the hole punching fails (the packets don't reach the right port). The direct connectivity checks fail (the symmetric NAT defeating direct traversal). A TURN-fallback case: since direct failed, ICE falls back to the relay candidates -- both peers connect outbound to the TURN server (which their NATs allow -- it's an outbound connection), and the TURN server relays traffic between them. The connectivity check for the relay path passes (both can reach the TURN server), so ICE selects the relayed path -- establishing the connection via TURN (relayed -- higher latency and server cost, but it works when direct fails). The TURN fallback (relaying) established the connection that direct couldn't (the symmetric NAT case) -- the robustness of the direct-then-relay approach.
The TURN-capacity and diversity vignettes complete it. A TURN-capacity case: a fraction of the connections (those with symmetric NATs or restrictive firewalls -- often 10-20%) fall back to TURN (relayed) -- so the team provisions TURN capacity for that fraction (TURN relays all the traffic for those connections -- so it needs sufficient bandwidth/capacity for the relayed fraction -- a real cost, sized for the expected fallback rate). A diversity case: the team tests across NAT types (cone, symmetric, restrictive -- and various real-world routers/carriers) -- ensuring the traversal works across the diversity (direct where possible, TURN for the hard cases) -- so connections establish reliably for real users (with their diverse NATs). The consolidated discipline the team documents: use ICE to orchestrate NAT traversal (gathering candidates -- host, server-reflexive/STUN, relay/TURN -- and connectivity checks to find the best path), prefer direct connections (STUN + hole punching -- low latency, no relay cost) with TURN as the fallback (relaying when direct fails -- symmetric NATs, restrictive firewalls), exchange candidates via signaling (out-of-band, since peers can't yet connect), provision TURN capacity for the fallback fraction (TURN relays all that traffic -- a real cost), handle NAT diversity (testing across NAT types -- ensuring traversal works), and ensure the fallback works (connections always establish) -- because NAT traversal (STUN discovering public addresses, hole punching for direct connections, TURN relaying as fallback, ICE orchestrating) is what makes peer-to-peer connectivity possible on the NAT-riddled internet, foundational to WebRTC, VoIP, gaming, and P2P.