Real-Time Backend
Signaling server
WebRTC sets up a direct P2P connection between two browsers through NAT, but someone has to introduce them first. The signaling server does it in 10-20 messages, then steps out of the picture.
- **Livekit** (open-source WebRTC platform) uses Redis pub/sub for signaling between instances: every WebSocket server subscribes to channels for its rooms and forwards messages through Redis. Scales horizontally to thousands of instances
- **Daily.co** generates short-lived TURN credentials (24h) via its signaling API. Every join returns fresh credentials, capping abuse of any single user's credentials by TTL
- **Twilio Video** publishes stats: its signaling servers handle peaks of 50M messages per day (SDP + ICE candidates) with minimal compute load - pure relay
- **Cloudflare Calls** offers managed signaling + TURN. No need to run your own server, just the client SDK and one API endpoint
Signaling role
A signaling server is a 'witness' during WebRTC connection setup. It doesn't process media, decode video, or store data. Its only job is to pass SDP and ICE candidates between two peers until they can talk directly.
WebRTC deliberately doesn't standardize the signaling protocol - that's left to the developer. You can use WebSocket, Server-Sent Events, even a QR code or email. In practice 99% of implementations use WebSocket for bidirectionality and low latency.
**Scale:** a signaling server for 10000 concurrent WebRTC sessions handles roughly 10-50 messages per session (SDP + ICE candidates). That's ~100-500K messages during a session peak. After connections are established the load drops to almost zero - just heartbeats. Signaling scales horizontally, unlike TURN.
After a successful Offer/Answer exchange and ICE negotiation, what role does the signaling server still play?
Signaling protocol
Even though WebRTC doesn't standardize signaling, a de facto standard has emerged in practice: JSON messages with a `type` field over WebSocket. Minimal type set: `offer`, `answer`, `ice-candidate`, `hangup`.
**Scaling signaling:** a simple relay works as long as all peers connect to the same server instance. For horizontal scaling you need pub/sub (Redis, NATS) for cross-instance relay. Livekit (open-source WebRTC platform) uses Redis pub/sub for signaling between instances.
The signaling server scales horizontally to 3 instances. Alice is on instance 1, Bob is on instance 2. How do you relay messages from Alice to Bob?
Perfect negotiation
In WebRTC a race condition isn't an edge case, it's a normal event. Alice creates an offer, Bob creates an offer at the same time. Both receive an offer from the other while still holding a local offer. That's **glare**: a collision of offers. Without handling glare the connection hangs.
**Perfect Negotiation** is a pattern from the W3C WebRTC spec for handling glare correctly without complex coordination. The idea: one peer is designated 'polite' (yields), the other is 'impolite' (insists). On glare the polite peer rolls back its own offer and accepts the offer from the impolite peer.
**Implicit rollback:** `await pc.setRemoteDescription(sdp)` when signalingState='have-local-offer' used to require an explicit `setLocalDescription({type:'rollback'})`. In modern browsers (Chrome 80+, Firefox 75+) setRemoteDescription does an implicit rollback automatically - which is what makes Perfect Negotiation concise.
In the Perfect Negotiation pattern, what does the 'polite' peer do when it receives an offer during glare (while creating its own offer)?
Signaling implementation
A production signaling server has to handle more than just basic relay: WebSocket authentication, room management, reconnect handling, rate limiting against DoS. Below is a production-ready structure.
**TURN credentials rotation:** static TURN credentials in code are a vulnerability. Production approach: the signaling server generates short-lived TURN credentials (TTL 24h) via the coturn/Cloudflare TURN REST API. The client requests credentials on join, gets `{ username, credential, ttl }`, and uses them for RTCPeerConnection. Twilio and Daily.co generate credentials on their backends exactly this way.
The signaling server is the TURN server; they do the same job
Signaling and TURN are different servers with different jobs: signaling exchanges SDP/ICE metadata (text, low traffic), TURN relays media streams (video/audio, high traffic)
After a connection is established, the signaling server is almost idle - only rare renegotiation events. The TURN server pushes megabits of video continuously during active sessions. They scale differently: signaling is a stateless WebSocket relay, TURN needs wide network bandwidth. Mixing them in one service is an architectural mistake.
Why shouldn't TURN server credentials be hardcoded into client code?
Key takeaways
- Signaling server is a relay for SDP/ICE metadata; after the P2P handshake its load drops to zero; scales horizontally via Redis pub/sub
- Perfect Negotiation resolves glare (offer collisions) through polite/impolite roles; the polite peer rolls back its offer when one arrives from the other side
- TURN credentials should be short-lived (24h TTL) and generated per-session by the signaling server; hardcoded credentials open the relay to abuse
Related topics
The signaling server is where WebRTC integrates with the rest of the backend:
- WebRTC basics — Previous lesson: P2P architecture, ICE, SDP - the foundation for understanding what signaling actually relays
- WebSocket server — Signaling uses WebSocket as transport; the same room management and message routing patterns apply to both
- WebSocket authentication — JWT in query parameters on WS upgrade is the standard auth pattern for signaling; same mechanisms as in the WS auth lesson
Вопросы для размышления
- The signaling protocol is not standardized in the WebRTC spec. What benefits and drawbacks does that freedom give the developer?
- Perfect Negotiation requires deciding who is 'polite' and who is 'impolite'. How do you do this in a group of 3+ participants where any of them can trigger renegotiation?
- On reconnect (after a connection break) do you have to start Offer/Answer from scratch, or can you continue with the existing RTCPeerConnection via ICE restart?
Связанные уроки
- rt-55 — Builds on the WebRTC handshake fundamentals (SDP, ICE) from the previous lesson
- rt-57 — STUN and TURN are the infrastructure the signaling server provisions credentials for
- net-36-websocket