NAT & Hole Punching

How devices find each other across the internet — from first principles, with live demos.

Act I

The Address Problem

Your home network probably has a phone, a laptop, maybe a smart TV, a tablet, a game console. Each one talks to the internet. But your Internet Service Provider gave your household just one public IP address.

So how does a reply from Netflix know to reach the TV and not your laptop? That's the job of your router — and the trick it uses is called Network Address Translation, or NAT.

Let's watch it work.

Click a device to send a request
Click a device to send a request through the router

▸ Try clicking each device. Watch how the router rewrites the source address before the packet leaves your network.

Every outbound packet gets its return address rewritten. Your device's private IP (like 192.168.1.5) becomes the router's public IP plus a port number the router picks. The server never sees your private address — it just sees the router.

NAT is a translator. It lets many private devices share one public address by rewriting packet headers and keeping a table of who's who.
Act II

The Translation Table

The router's secret weapon is a simple table. Every time a device sends a packet out, the router creates an entry: "private IP:port → public IP:assigned port → destination." When a reply comes back, it looks up the assigned port to figure out which device to forward it to.

Let's build a NAT table from scratch.

Create connections:

▸ Try creating several connections. Notice how each one gets a unique external port — that's the only thing distinguishing these devices from the outside.

From the server's perspective, all it sees is 203.0.113.50:48721 or 203.0.113.50:48722. It has absolutely no idea there are multiple devices behind that IP. The port is the only differentiator — and the mapping between port and device is known only to the router.

This works beautifully for outbound connections. Your laptop asks for a webpage, the router tracks it, the reply flows back. Simple.

But there's a catch.

Act III

The Wall

What happens if someone on the internet wants to reach your laptop without your laptop sending anything first?

They send a packet to your public IP. It arrives at your router. The router looks up the destination port in its NAT table… and finds nothing. No device ever created a mapping for that port. So the router does the only sensible thing: it drops the packet.

Try it yourself.

Try sending a packet from the outside
Send an unsolicited inbound packet:

▸ First, try sending to any port — watch it get dropped. Then click "laptop sends out" to create a mapping, and try that port again.

This is exactly why peer-to-peer connections are hard. If your friend wants to connect to your Minecraft server, their packet arrives at your router, and the router says: "I have no idea who this is for." Dropped.

Both players are behind NATs. Both routers are dropping unsolicited packets. Neither side can reach the other. It's a standoff.

NAT creates an asymmetry: outbound connections are easy, inbound connections are impossible — unless you can somehow trick the router into creating a mapping first.

So how do we trick it?

Act IV

The Mirror

Before two peers can connect, each needs to know a crucial piece of information: their own external address. From behind a NAT, you can't see what IP and port the outside world sees — your device only knows its private IP.

The solution is almost embarrassingly simple. You send a packet to a public server, and the server tells you what address the packet came from. It's like calling someone and asking: "Hey, what number showed up on your caller ID?"

This is called STUN — Session Traversal Utilities for NAT. Let's watch it work.

Click to query the STUN server
Discover your external address:

▸ Watch the packet go out, get rewritten by NAT, and the STUN server reflect the external address back.

The STUN server does almost nothing — it just mirrors your packet's source address back to you. But now you know something you couldn't know before: what you look like from the outside.

This is the first ingredient of the trick. Both peers learn their external IP:port via STUN. Now they need a way to share this info with each other.

Act V

The Trick

Here's where it gets clever. We know that sending a packet out creates a NAT mapping. And we know that once a mapping exists, packets coming in to that port will be forwarded. So what if both peers send packets to each other at the same time?

The process works in five steps. Let's walk through each one.

▸ Step through each phase. Pay attention to how each outbound packet "punches a hole" in its own NAT — and how the other peer's packet eventually finds that hole.

The magic moment is step 4. Peer A's packet arrives at NAT B, and for the first time, there's a mapping waiting for it — because Peer B just sent a packet to Peer A, which created that mapping. The hole has been punched.

Each outbound packet opens a hole in the sender's NAT. If both sides open holes pointed at each other at roughly the same time, the packets start getting through. That's hole punching.

Once the first packet makes it through in each direction, both NATs have active mappings. The peers can now talk directly — no middleman, no relay. Just two devices, across the internet, as if the NATs weren't there.

Act VI

When It Breaks

Hole punching doesn't always work. It depends on how strict each router's NAT is. There are several types, and each one is more paranoid than the last.

NAT type:

▸ Toggle between NAT types. Watch how each one applies stricter rules about which incoming packets it allows through.

Full Cone is the most relaxed: once a mapping exists, anyone can send packets to that external port. Hole punching works easily.

Address-Restricted adds a check: the incoming packet's source IP must match the IP the mapping was created for. You can only receive from IPs you've already sent to.

Port-Restricted goes further: both the IP and the port must match. Hole punching still works because both peers send to each other's exact endpoint.

Symmetric NAT is the killer. It assigns a different external port for every destination. So the port that the STUN server sees is not the same port that Peer B would see. The address you learned from STUN is useless — the peer can't predict which port to send to.

Symmetric NAT defeats hole punching because it changes the external port for every new destination. The peer has no way to know which port to target.

When hole punching fails, there's a fallback: a TURN server (Traversal Using Relays around NAT). This is just a public server that both peers connect to, which relays packets between them. It always works, but adds latency and costs bandwidth — the traffic goes through a middleman instead of flowing directly.

Act VII

The Real World

Every time you make a video call, play an online game, or use a VPN, this machinery is running under the hood. Here's the decision tree that systems like WebRTC use:

The ICE connection decision tree

WebRTC — the technology behind Google Meet, Discord voice chat, and screen sharing — uses exactly this process. It's called ICE (Interactive Connectivity Establishment): try direct first, then STUN + hole punch, then fall back to TURN relay.

Tailscale and WireGuard take a similar approach for VPN connections. Tailscale's coordination servers act as the rendezvous point, and they implement hole punching automatically. When it works, your VPN traffic flows directly between devices. When it doesn't, it routes through relay servers they call DERP (Designated Encrypted Relay for Packets).

Game servers usually skip the complexity entirely — they use a public server with a known address. That's why you port-forward for Minecraft: you're creating a permanent NAT mapping so players can find your machine. Services like Playit.gg and Ngrok create a tunnel from your machine to a public relay, avoiding port forwarding at the cost of routing everything through their infrastructure.

NAT was a workaround for running out of IPv4 addresses. Every trick we've discussed — STUN, hole punching, TURN relays, port forwarding, tunnels — is a workaround for the workaround. IPv6, where every device gets a globally routable address, would make all of this unnecessary. But we're still waiting.