WebRTC enables real-time audio, video, and data communication between browsers and other compatible endpoints. It is commonly used in video meetings, voice calls, screen sharing, and peer-to-peer applications.
However, an established WebRTC connection can become unusable when a device changes networks. For example, a user may switch from Wi-Fi to mobile data, reconnect after a temporary network outage, or move between networks with different NAT and firewall configurations.
In these situations, the existing network path may no longer work. Simply keeping the same RTCPeerConnection alive does not guarantee that media will continue flowing.
WebRTC provides a mechanism called an ICE restart to help recover connectivity. It allows peers to gather and exchange new Interactive Connectivity Establishment (ICE) credentials and candidates while retaining the existing peer connection.
This article explains how ICE restart works, when to use it, how to implement it in JavaScript, and how to avoid common recovery problems.
Understanding ICE in WebRTC
ICE stands for Interactive Connectivity Establishment. It helps WebRTC peers discover and establish a usable network path for communication.
Two devices may be behind different routers, NAT devices, or firewalls. Their private IP addresses are not necessarily reachable from the public internet. ICE helps identify possible connection paths and test which ones can carry traffic.
WebRTC commonly uses three types of ICE candidates.
| Candidate type | Purpose |
|---|
| Host | Represents a local network address available to the endpoint |
| Server reflexive | Represents an address discovered through a STUN server |
| Relay | Represents an address provided by a TURN server |
A direct host or server-reflexive path may work in some network configurations. In more restrictive environments, a TURN relay may be required.
During connection establishment, the peers exchange session descriptions and ICE candidates through an application-controlled signaling channel. The signaling mechanism might use WebSockets, an HTTP-based service, or another communication system.
ICE performs connectivity checks to identify a usable candidate pair. Once a path is selected, the connection can carry media or data.
The signaling channel is separate from the media connection. This distinction becomes important when implementing recovery: an ICE restart requires the peers to exchange new information through signaling, even though the existing media path may be broken.
Why Network Changes Break WebRTC Connections
A WebRTC connection depends on the network path selected during ICE negotiation. When the underlying network changes, that path may stop working.
Consider a video call running on a laptop connected to Wi-Fi. The laptop switches to a mobile hotspot, receives a different IP address, and moves to a network with different NAT behavior.
The existing candidate pair may no longer be valid. Media packets may stop arriving, even though the browser still reports an established peer connection.
Common causes include:
- Switching from Wi-Fi to mobile data.
- Losing connectivity temporarily and reconnecting.
- Changing VPN connections.
- Moving between networks with different firewall policies.
- Changes to NAT mappings or available network interfaces.
Not every network change requires an ICE restart. ICE may recover automatically in some situations, and the existing connection might remain usable.
An application should monitor connection state and media activity before deciding that a restart is necessary.
What Is an ICE Restart?
An ICE restart allows WebRTC peers to begin a new ICE connectivity negotiation on an existing RTCPeerConnection.
The restart generates new ICE username fragments and passwords, commonly called ufrag and pwd. The peers exchange updated session descriptions and candidates through signaling, then perform connectivity checks using the new ICE information.
In JavaScript, the simplest way to request an ICE restart when creating an offer is:
const offer = await peerConnection.createOffer({
iceRestart: true
});
The resulting offer contains new ICE credentials for the restart. The offer must still be sent to the remote peer through the application's signaling system, and the remote peer must process it correctly.
An ICE restart does not create a completely new RTCPeerConnection. It is intended to recover connectivity while retaining the existing peer connection and its associated media and data-channel configuration.
However, it is not guaranteed to succeed. The peers still need a working signaling path, compatible ICE implementations, and at least one viable network route.
Implementing ICE Restart in JavaScript
Let's build a simplified recovery flow for a browser-based WebRTC application.
The example assumes that an application already has a configured RTCPeerConnection, a signaling channel, and a remote peer capable of processing renegotiation messages.
Create the peer connection
const peerConnection = new RTCPeerConnection({
iceServers: [
{
urls: "stun:stun.example.com:3478"
}
]
});
The hostname above is illustrative. Replace it with a STUN server appropriate for your application.
For production deployments, evaluate whether a TURN server is required. A STUN server helps discover network addresses, but it does not relay media when direct connectivity is unavailable.
Monitor connection state
The connectionState property provides an overall indication of the peer connection's state.
peerConnection.addEventListener(
"connectionstatechange",
() => {
console.log(
"Connection state:",
peerConnection.connectionState
);
if (
peerConnection.connectionState === "failed"
) {
console.log(
"Connection failed. Recovery may be required."
);
}
}
);
The failed state is a reasonable signal to consider an ICE restart. However, applications should avoid immediately creating repeated offers whenever a state-change event occurs.
A production implementation should prevent concurrent restart attempts and distinguish a temporary interruption from a persistent failure.
The disconnected state is different from failed. It can be temporary, so restarting ICE immediately whenever the connection becomes disconnected may cause unnecessary renegotiation.
Create and send an ICE restart offer
The following function initiates the local side of an ICE restart:
async function restartIce() {
if (
peerConnection.signalingState !== "stable"
) {
console.log(
"Cannot restart ICE while signaling is busy."
);
return;
}
const offer = await peerConnection.createOffer({
iceRestart: true
});
await peerConnection.setLocalDescription(offer);
sendSignal({
type: "ice-restart-offer",
description: peerConnection.localDescription
});
}
Here, sendSignal() represents an application-specific signaling function. It must deliver the message to the correct remote peer.
The signaling-state check helps avoid starting a new offer while another negotiation is in progress. It is not a complete negotiation strategy: real applications must also handle simultaneous offers, retries, timeouts, and signaling messages that arrive out of order.
Notice that the function sets the local description before sending the offer. This allows the peer connection to begin gathering candidates associated with the new description.
The next step is to process the offer and response on the remote side.
Handling the ICE Restart Offer on the Remote Peer
The receiving peer must apply the new remote description and create an answer.
async function handleIceRestartOffer(description) {
await peerConnection.setRemoteDescription(
description
);
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
sendSignal({
type: "ice-restart-answer",
description: peerConnection.localDescription
});
}
The caller must then process the answer:
async function handleIceRestartAnswer(description) {
await peerConnection.setRemoteDescription(
description
);
}
These functions demonstrate the core exchange, but a complete application must route messages to the correct peer and handle errors, duplicate messages, and competing negotiations.
An ICE restart is a negotiation between both peers, not a local operation that can independently repair the entire connection.
Handling ICE Candidates During Restart
ICE candidates may be gathered and exchanged while the new negotiation is underway.
A common pattern is to send candidates as they become available:
peerConnection.addEventListener(
"icecandidate",
(event) => {
if (event.candidate) {
sendSignal({
type: "ice-candidate",
candidate: event.candidate
});
}
}
);
The remote peer receives the message and adds the candidate:
async function handleIceCandidate(candidate) {
await peerConnection.addIceCandidate(candidate);
}
In a production application, candidate handling must be coordinated with session-description processing. Candidates can arrive before the corresponding remote description has been applied.
One approach is to queue incoming candidates until the required remote description is available, then process them in order. The application should also handle candidates associated with different ICE generations so that stale candidates from an earlier negotiation are not incorrectly applied to a newer one.
Depending on the signaling design, the application may exchange candidates individually or send a session description after candidate gathering completes. Either approach requires a consistent protocol on both peers.
When Should an Application Restart ICE?
An ICE restart should be based on connection behavior rather than every network-related event.
The following situations may justify attempting recovery:
- The peer connection enters the
failed state. - The application detects a persistent loss of media connectivity.
- A network change is detected and communication does not recover.
- The current candidate pair is no longer usable.
The browser's iceConnectionState property can provide additional detail about the ICE subsystem.
peerConnection.addEventListener(
"iceconnectionstatechange",
() => {
console.log(
"ICE state:",
peerConnection.iceConnectionState
);
}
);
Values such as checking, connected, completed, disconnected, and failed help the application determine what is happening.
Avoid using one fixed, short delay as the only recovery policy. Network conditions vary, and an interruption may resolve before a restart is needed.
A more robust strategy combines state changes, media-flow monitoring, a timeout policy, and a limit on repeated restart attempts.
Common Problems and Their Solutions
| Problem | Possible cause | Recommended action |
|---|
| ICE restart offer is created but connectivity does not recover | No usable candidate pair exists | Check STUN/TURN configuration and network restrictions |
| Remote peer never answers | Signaling message was lost or the peer cannot process the offer | Verify signaling delivery and remote negotiation handling |
| Restart offers repeatedly fail | Multiple negotiations are overlapping | Serialize negotiation and implement a recovery state machine |
| Candidates are rejected | Candidates arrive before the remote description or belong to an older ICE generation | Queue and validate candidates against the current negotiation |
| Connection remains disconnected | The network interruption may be temporary | Monitor state and media activity before retrying |
| Media does not return after negotiation | Connectivity checks failed or media tracks were stopped | Inspect ICE state, selected candidate pair, and track state |
An ICE restart cannot compensate for a broken signaling channel. If the peers cannot exchange the new offer, answer, and relevant candidates, the restart cannot complete.
Best Practices for Production Applications
Keep signaling independent from media
Use a signaling mechanism that can remain available when the WebRTC media path fails. A WebSocket connection or another signaling service can help peers exchange updated session descriptions and candidates.
If signaling itself fails during a network change, reconnect it before attempting further negotiation.
Use TURN where necessary
STUN helps discover candidate addresses, but it does not guarantee that peers can communicate through restrictive NATs or firewalls.
A properly configured TURN service provides relay candidates when direct connectivity is not possible. Monitor relay usage and plan capacity for expected call volume.
Prevent repeated restart attempts
Use a recovery state machine to ensure that only one ICE restart is active at a time. Apply bounded retries and backoff rather than generating offers continuously.
If recovery fails after a reasonable number of attempts, notify the user or offer to establish a new peer connection.
Monitor connection quality
Track ICE state, connection state, selected candidate-pair information where available, packet loss, round-trip time, and inbound or outbound media statistics.
These measurements help distinguish signaling failures from network connectivity problems and media-processing issues.
Test real network transitions
Test switching between Wi-Fi and mobile networks, VPN changes, temporary connectivity loss, and restrictive network configurations.
Also test both peers changing networks simultaneously. A recovery strategy that works for one-sided network changes may fail when both endpoints initiate negotiation at the same time.
Advantages and Disadvantages of ICE Restart
ICE restart has several advantages:
- It can recover connectivity without creating an entirely new peer connection.
- It allows peers to gather new network candidates and credentials.
- It can preserve existing media and data-channel configuration.
- It supports recovery from network changes and failed candidate paths.
There are also limitations. ICE restart requires successful signaling and a viable network route. It introduces additional negotiation and connectivity-check traffic, and simultaneous restart attempts can complicate signaling. It may also fail when the network blocks all usable paths or the TURN infrastructure is unavailable.
For these reasons, ICE restart should be one part of a broader connection-recovery strategy rather than the only mechanism for handling network failures.
Summary
WebRTC ICE restart provides a way to renegotiate network connectivity when an established connection can no longer communicate through its existing path.
The essential process is to create an offer with iceRestart: true, apply and exchange session descriptions, handle ICE candidates for the new negotiation, and monitor the resulting connection state.
A reliable implementation also needs signaling recovery, concurrency control, candidate-generation handling, appropriate STUN/TURN configuration, and bounded retry logic.
By treating ICE restart as a coordinated recovery process between both peers, developers can build real-time applications that handle network changes more reliably without unnecessarily recreating every peer connection.
Join the conversation! Your thoughts help the community grow.