How WebRTC finds a path between peers
Before media can flow, WebRTC’s ICE (Interactive Connectivity Establishment) agent gathers every address at which each peer might be reachable, called candidates, then probes pairs of them to pick the best working route. This reference explains the four candidate types, the SDP attribute that encodes them, and how each fits into NAT traversal — plus a parser for any candidate line you paste in.
The four candidate types
| Type | How it is gathered | When it works |
|---|---|---|
host | Local network interfaces (LAN IP or mDNS .local) | Peers on the same network |
srflx (server-reflexive) | A STUN server observes your public IP/port | Peers behind cone NATs |
prflx (peer-reflexive) | Discovered during STUN connectivity checks | Created on the fly; never signalled |
relay | Address on a TURN server that forwards packets | Symmetric NATs and strict firewalls |
ICE tries candidates in priority order — host first, then srflx, then relay — and picks the first pair that produces a successful STUN binding response. The prflx type is special: it is never included in the initial SDP offer/answer but is created at runtime if a NAT assigns a new port for an incoming STUN probe.
How a candidate line is structured
Each candidate is encoded as a single SDP attribute:
a=candidate:842163049 1 udp 1677729535 203.0.113.5 54123 typ srflx raddr 192.168.1.7 rport 54123
Reading left to right: foundation (groups candidates from the same STUN server), component (1 = RTP, 2 = RTCP), transport (udp or tcp), priority (higher = preferred), connection address, port, the keyword typ, candidate type. Server-reflexive and relayed candidates also carry raddr/rport pointing back at the local base address the STUN or TURN request was sent from.
Priority is computed from a formula that weights type first (host > srflx > relay), then local preference, then component. The ICE agent pairs every local candidate with every remote candidate, sorts by combined priority, and runs STUN binding requests in order.
Parsing and debugging candidate lines
To decode a candidate line: paste it into the parser at the top of this page. The tool extracts each field and names it so you can quickly confirm type, address, transport, and priority without counting space-delimited tokens manually. This is useful when you need to verify that a TURN server is actually producing relay candidates, or to confirm that mDNS obfuscation is hiding the host IP as intended.
Practical guidance
- Always provide a TURN server. In real deployments a significant fraction of peer pairs cannot form a direct path — symmetric NAT on either side blocks both host and srflx paths. Without a relay, those calls simply fail.
- Use TCP TURN as a fallback. Some corporate firewalls block UDP entirely. A TCP or TLS TURN relay on port 443 bypasses most such restrictions.
- Check gather state, not just ICE state. The
RTCPeerConnection.iceGatheringStateevent sequence (new→gathering→complete) tells you when all candidates have been collected;iceConnectionStatetells you when a pair was selected. - mDNS
.localhostnames are intentional. Browsers replaced LAN IPs in host candidates with random mDNS hostnames to prevent fingerprinting. Peers resolve them via local mDNS; they do not leak the real IP to the signalling server. - Peer-reflexive candidates appear only during the handshake and are never signalled ahead of time.