STUN, TURN and ICE, Explained Without Jargon

By ShareDrop.org Engineering · Published · Updated

Every explanation of WebRTC eventually drops three acronyms (STUN, TURN, ICE), and most readers' eyes glaze over. The ideas behind them are actually simple, and knowing them tells you exactly why a transfer is fast, slow, or impossible on a given network. This article goes one level deeper than the usual summary, because ShareDrop.org operates its own STUN and TURN servers, and the details are where the behaviour you see comes from.

The problem: your device does not know its own address

At home, your laptop's address is something like 192.168.1.23 — valid only inside your Wi-Fi. The wider internet sees your router's public address instead, and the router only forwards incoming traffic that it recognizes as a reply to something you sent. Two devices in this situation cannot simply dial each other: neither knows a number the other could call, and neither would answer an unexpected call if it did (how NAT decides what to let in).

STUN: a mirror

A STUN server does one tiny job: you send it a packet, and it replies “here is the public address and port your packet appeared to come from.” Now your browser knows what it looks like from the outside. If both sides learn their public addresses and start sending to each other more or less simultaneously, many routers accept the traffic as replies — the classic “hole punching” trick. STUN servers carry almost no traffic and see no file data; they are address mirrors, nothing more.

Concretely, the exchange is a Binding request of about twenty bytes over UDP, usually to port 3478, and a Binding response carrying anXOR-MAPPED-ADDRESS attribute: the reflected address, XOR-ed with a magic cookie so that overzealous routers do not rewrite it in transit. Public STUN servers exist and browsers will happily use them, but every TURN server is also a STUN server, so ShareDrop.org points both roles at its own relay and avoids depending on a third party for the mirror.

TURN: a relay for when mirrors are not enough

Some networks defeat hole punching: symmetric NATs use a different public port for every destination, carrier-grade NAT stacks multiple layers, and strict firewalls block UDP outright (why direct connections fail). TURN is the fallback: a server that both sides can reach, which forwards traffic between them. It costs real bandwidth for the operator and adds a hop of latency, but critically, in WebRTC the traffic it forwards is already encrypted end-to-end. A TURN server cannot read what it relays; it only sees packet sizes, timing and addresses (direct vs relay).

How an allocation works

  1. Allocate. The browser authenticates to the TURN server and asks for a relayed address. The server reserves a UDP port on its public side, say 203.0.113.50:49200, and hands it back. That address becomes one of the browser's ICE candidates (type relay).
  2. Permission. The relay will not forward packets from just anyone to that port. The browser must first install a permission for the peer's address; permissions last five minutes and are refreshed while the connection is in use.
  3. Data. Early packets travel wrapped in STUN messages (Send and Data indications). Once the path is established, the browser binds a channel, a two-byte number, and each packet then costs only a four-byte header. This is why a relayed transfer is nearly as efficient on the wire as a direct one.
  4. Lifetime. An allocation expires after ten minutes unless refreshed; the browser refreshes it while the connection lives and lets it lapse afterwards. Nothing about the session survives on the server.

The browser talks to the relay over UDP, TCP, or TLS: the last two on port 443 specifically to survive networks that only allow “normal HTTPS” traffic. The relayed side toward the peer is always UDP. ShareDrop.org's relay currently offers UDP and TCP; when a network blocks UDP entirely, the transfer connects over TCP to the relay and still completes.

What a relay costs, and why free ones are rare

Every relayed byte enters the server and leaves it again, so a 1 GB transfer is 2 GB of server traffic. Multiply by users and the relay is the single largest running cost of a peer-to-peer service, which is why so many tools quietly have no relay at all and simply fail on networks that need one. ShareDrop.org runs its own (coturn) rather than a third-party service, so that the relay's logging policy stays in the service's own hands: IP addresses in rate-limiting logs for at most 24 hours, nothing else.

Credentials that expire in minutes

A relay that accepts anyone would be abused within hours. The standard answer is the TURN REST scheme: the signaling server hands each session a username of the form expiry-timestamp:session-id and a password that is an HMAC of that username under a secret shared with the relay. The relay can verify the password without a database lookup, and the credential is worthless after the timestamp — a few minutes later. Nobody stores a password; nobody can reuse a leaked one.

ICE: the tournament

ICE is the process that ties this together. Each browser gathers every address it might be reachable at, both sides exchange these candidate lists through the signaling channel, and then they systematically test candidate pairs with small probe packets until one works.

Candidate typeSourceType preferenceMeaning if it wins
hostLocal interface address126 (highest)Same network, or public IPv6 — LAN direct
prflx (peer-reflexive)Discovered during checks110Direct; the router revealed a port during testing
srflx (server-reflexive)STUN mirror100Direct across the internet by hole punching — Internet direct
relayTURN allocation0 (lowest)Forwarded by the relay — Encrypted relay

Each candidate gets a priority computed from its type preference, a local preference (which interface: Ethernet beats Wi-Fi beats cellular) and its component. Pairs inherit a combined priority from both sides, and the checks run in that order, so a host-to-host pair is tried before anything involving the relay, and the relay wins only when everything above it has failed.

Three candidates, annotated

Here is what a browser actually sends the other side — one line per candidate:

a=candidate:1 1 udp 2122260223 192.168.1.23 51234 typ host
a=candidate:2 1 udp 1686052607 203.0.113.7 41000 typ srflx raddr 192.168.1.23 rport 51234
a=candidate:3 1 udp 41885439 203.0.113.50 49200 typ relay raddr 203.0.113.7 rport 41000

The peer receives all three, pairs each with its own candidates, and the tournament begins with the host-to-host pair. On the same network that pair wins in tens of milliseconds; across the internet it fails and the reflexive pair takes over; behind a symmetric NAT the reflexive pair fails too and the relay pair carries the session.

The checks themselves

A connectivity check is a STUN Binding request sent from one candidate to the other, authenticated with the short-term credentials (ice-ufrag and ice-pwd) exchanged in the session description: a stray packet from the wrong session fails authentication and is ignored. A pair whose check is answered is succeeded; the controlling side then nominates the pair it wants, the other side agrees, and the data channel opens on it. Checks that never get an answer time out; the whole tournament typically takes a few hundred milliseconds on two home networks and a few seconds when only a relay works.

Trickle ICE and restarts

Modern implementations use trickle ICE: candidates are sent to the other side as they are discovered rather than all at once, and checks begin on the first pair before the last candidate exists. A host candidate is known instantly; a reflexive one needs a STUN round trip; a relay candidate needs an allocation. Trickle lets a LAN connection complete before the relay allocation even finishes, which is why LAN direct appears almost immediately.

When the network changes underneath a live connection, a phone leaving Wi-Fi for cellular, the browser performs an ICE restart: new credentials, a fresh gathering, a new tournament, while the DTLS session and data channel persist. In ShareDrop.org a restart that succeeds is invisible; one that fails becomes a reconnect through signaling, and the transfer resumes from the last verified chunk (where that fits in the whole flow).

A privacy detail: hidden host addresses

Browsers no longer put your raw local address (192.168.1.23) into host candidates, because web pages used it for fingerprinting. Instead the candidate carries a random name ending in .local, which the peer resolves with multicast DNS, something that only works when both devices are on the same network, which is the only case a host candidate is useful for anyway. The side effect is that a LAN-direct connection now depends on multicast DNS working: a network that blocks multicast, or a Mac that has not granted the browser local-network permission, falls back to a relay for two devices sitting side by side (managed networks).

How long things take, and why “connecting” sometimes hangs

ICE is fast when something works and slow when nothing does, because it cannot know the difference between a check that is still in flight and one that will never be answered. A single check is retransmitted with increasing gaps for several seconds before that pair is declared failed; with a dozen pairs to try, a connection where no path works can take tens of seconds to conclude that. This is the “connecting…” that seems to hang: the browser is still dutifully waiting for answers that a firewall is silently discarding. ShareDrop.org gathers relay candidates from the start so the fallback is tried early rather than after every direct pair has timed out, and it reports a failure once ICE does.

After a connection is up, the browsers keep checking. Every few seconds each side sends a small consent request (RFC 7675); if thirty seconds pass with no reply, the connection is declared failed rather than left to hang. In between, a brief loss of replies puts the connection in a disconnected state (a phone passing through a lift, a laptop switching access points) from which it usually recovers on its own. A transfer tool that treats disconnected as fatal restarts transfers that would have resumed by themselves two seconds later; ShareDrop.org waits.

Reading your own connection

In ShareDrop.org, the connection badge is derived from the candidate pair that actually won, read live from WebRTC statistics: two host candidates mean a LAN transfer, a reflexive pair means direct across the internet, and any relay candidate means TURN is in the path. The mode is never inferred from configuration, only from what the connection reports about itself.

To see the same thing yourself in Chrome, open chrome://webrtc-internals during a transfer, expand the connection, and look for the candidate-pair entry with state: succeeded and nominated: true. Its localCandidateId and remoteCandidateId point at two candidate entries whose candidateType fields are the answer; its bytesReceived counter, sampled a second apart, is your true throughput. Firefox exposes the same under about:webrtc.

Why three protocols instead of one

The split looks like committee design, but it follows the problem. STUN came first, in 2003, with the modest job of telling a device its own public address; its original version also tried to classify NATs into the four types, which turned out to be unreliable, and later revisions dropped the classification and kept the mirror. TURN followed as a separate protocol because relaying is a fundamentally different service with a fundamentally different cost: a mirror answers one packet; a relay carries every byte for the life of the session and must authenticate, meter and expire. ICE arrived to orchestrate the two: it is not a wire protocol of its own so much as a procedure for gathering, exchanging and testing addresses, and it would work with any mirror and any relay. Browsers hide the entire arrangement behind one object, RTCPeerConnection, which is why most web developers never meet the three names until something fails.

IPv6 and dual-stack networks

When a device has both an IPv4 and an IPv6 address, it gathers host candidates for each, and its reflexive and relay candidates come in both flavours where the servers support them. ICE interleaves the two families when it orders checks, so an IPv6 pair (which needs no NAT traversal at all, only a firewall that accepts the check) often succeeds before the IPv4 pairs have finished trying. Two phones on IPv6-capable mobile carriers can therefore connect Internet direct despite both sitting behind carrier-grade NAT on the IPv4 side. The catch is that both ends need working IPv6; one without forces the whole tournament onto IPv4 (where the NAT rules apply).

What the page itself can see

A web page driving WebRTC gets a small window into all of this. Three state values on the connection tell it where things stand: iceGatheringState (is address discovery still running), iceConnectionState (has a pair been found, and is it still alive) and connectionState (the combined picture including DTLS). Beyond those, getStats() returns the full table of candidates and pairs with their types, states and byte counters, updated as the connection runs. ShareDrop.org reads exactly two things from it: which pair is nominated, to label the badge, and the bytes counter, to show the rate. It writes nothing back: a page can observe the negotiation but cannot bend it, which is part of why the whole scheme is safe to expose to arbitrary websites.

Running your own relay: what matters

ShareDrop.org runs coturn, the open-source TURN server that most of the industry runs. A few decisions matter more than the rest.

Field notes

Standards referenced