If you've ever used a browser-based softphone, a click-to-call widget, or a web-based contact center agent interface, you've used SIP over WebSockets and WebRTC together, whether you knew it or not. These two technologies solve complementary halves of the same problem: how do you make a real phone call from inside a web browser?
The answer is more interesting than you might expect, because browsers were never designed for real-time communications. Every piece of this stack exists because of a specific constraint that browsers impose.
The fundamental problem
SIP was designed for a world where endpoints could open UDP or TCP sockets on arbitrary ports. A SIP phone typically listens on UDP port 5060. It sends and receives SIP messages directly. It opens separate UDP ports for RTP media. This is fine for hardware phones and softphone applications running on a desktop operating system.
Browsers can't do any of that.
A web page running in Chrome or Firefox has no ability to open raw UDP or TCP sockets. It can make HTTP requests, open WebSocket connections, and use a handful of other browser APIs, but it cannot send a UDP packet to port 5060 on some SIP server. This is a security feature, not a bug. Letting arbitrary web pages open raw network sockets would be a catastrophe. But it means that traditional SIP transport (UDP, TCP, or TLS over TCP) is completely unavailable from a browser context.
So if you want SIP signaling in a browser, you need a transport that browsers actually support. That transport is WebSockets.
SIP over WebSockets: RFC 7118
RFC 7118, published in 2014, defines how to carry SIP messages over WebSocket connections. The concept is straightforward: the browser opens a WebSocket connection (typically WSS, the TLS-encrypted variant) to a SIP server that supports WebSocket transport. Once the connection is established, standard SIP messages are sent and received as WebSocket text frames.
The SIP messages themselves are the same SIP messages you'd see on any other transport. A REGISTER is still a REGISTER. An INVITE still contains SDP. The authentication challenges work the same way. The only difference is the transport layer carrying those messages.
A SIP message over WebSocket includes a Via header that indicates the transport:
Via: SIP/2.0/WSS df7jal23ls0d.invalid;branch=z9hG4bK56sdasks
That WSS tells every SIP entity in the path that this message arrived via a secure WebSocket connection. The domain in the Via header is typically a random identifier rather than a real hostname, since the browser doesn't have a routable address where it can receive inbound connections.
This is an important difference from traditional SIP. A desk phone has an IP address and a port. You can send it a SIP message at any time. A browser-based client is reachable only through its WebSocket connection to the server. The server acts as the anchor point, maintaining that persistent connection and routing messages to the browser through it.
How WebRTC fits in
SIP over WebSockets solves the signaling problem, but signaling is only half of a phone call. The other half is the actual audio. You need a way to capture audio from the user's microphone, encode it, encrypt it, send it across the network in real time, receive audio from the other party, decode it, and play it through the speakers. All with latency low enough for a conversation to feel natural.
This is what WebRTC does.
WebRTC (Web Real-Time Communication) is a browser API and protocol suite that handles real-time media. It's built into every modern browser. When a browser-based softphone places a call, SIP over WebSockets handles the call setup and teardown, while WebRTC handles everything about the actual audio stream.
The two are connected through SDP. When the browser's SIP stack sends an INVITE, the SDP body is generated by the browser's WebRTC engine. That SDP contains the media parameters that WebRTC will use: codecs, ICE candidates for NAT traversal, DTLS fingerprints for encryption, and the ports where the browser wants to receive media.
Here's a simplified view of what the SDP in a browser-originated INVITE looks like:
v=0
o=- 4858location 2 IN IP4 0.0.0.0
s=-
t=0 0
a=group:BUNDLE audio
m=audio 9 UDP/TLS/RTP/SAVPF 111 0 8
c=IN IP4 0.0.0.0
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=ice-ufrag:aR9l
a=ice-pwd:fE82jkS03bq1mMdoZ8Gy6f
a=fingerprint:sha-256 D1:2B:3C:...
a=setup:actpass
a=candidate:1 1 udp 2113937151 192.168.1.50 54321 typ host
a=candidate:2 1 udp 1845501695 203.0.113.50 62432 typ srflx raddr 192.168.1.50 rport 54321
A few things stand out compared to traditional SIP SDP. The media line specifies UDP/TLS/RTP/SAVPF rather than plain RTP/AVP, because WebRTC mandates encryption via DTLS-SRTP. The ICE candidates appear directly in the SDP (or are trickled in afterward). The a=fingerprint line provides the DTLS certificate fingerprint for end-to-end encryption verification. And the first codec listed is almost always Opus.
How a browser-based call actually flows
Let's trace a complete call from a browser-based softphone to a traditional SIP desk phone. This involves several systems working together.
1. WebSocket connection and registration
When the web softphone loads, it opens a WSS connection to the SIP server (e.g., wss://sip.example.com/ws). Over this connection, it sends a SIP REGISTER to authenticate and announce its presence:
REGISTER sip:example.com SIP/2.0
Via: SIP/2.0/WSS j4k8s9d2f.invalid;branch=z9hG4bK74bf9
Contact: <sip:[email protected];transport=ws>
Authorization: Digest username="alice", ...
The server authenticates the request and responds with 200 OK. Alice's browser is now registered and can make and receive calls.
2. The INVITE
Alice clicks a button to call Bob. The browser's SIP library creates a WebRTC peer connection, generates an SDP offer from the WebRTC engine, wraps it in a SIP INVITE, and sends it over the WebSocket:
INVITE sip:[email protected] SIP/2.0
Via: SIP/2.0/WSS j4k8s9d2f.invalid;branch=z9hG4bKnew1
Contact: <sip:[email protected];transport=ws>
Content-Type: application/sdp
[WebRTC-generated SDP with ICE candidates, DTLS fingerprint, Opus codec]
3. The gateway problem
If Bob is on a traditional desk phone, there's a mismatch. The browser is offering WebRTC media (Opus, DTLS-SRTP, ICE), and Bob's phone expects traditional RTP (G.711, SRTP or plain RTP, no ICE). Something needs to bridge that gap.
This is where Session Border Controllers (SBCs) and WebRTC gateways come in. The SIP server or an SBC in the path terminates the WebRTC media session on one side and establishes a traditional RTP session on the other. It transcodes Opus to G.711, terminates DTLS-SRTP and re-encrypts (or doesn't) as SRTP or plain RTP, and handles the ICE negotiation on the browser side while using a simple static media address toward the desk phone.
4. Media flows
Once the call is established (200 OK, ACK), audio flows. On the browser side, WebRTC handles everything: ICE connectivity checks complete, the DTLS handshake establishes encryption keys, and SRTP audio packets start flowing directly between the browser and the SBC or media gateway. On the desk phone side, standard RTP flows as it always has.
5. Hangup
Either side sends a BYE through the signaling path. The browser sends it over WebSocket, the desk phone sends it over UDP or TCP. The gateway handles the translation in both directions.
Browser SIP stacks
You don't implement SIP over WebSockets from scratch. Several mature JavaScript libraries handle the SIP protocol and WebRTC integration:
JsSIP is one of the original browser SIP stacks, lightweight and focused on the core SIP functionality. It handles SIP message parsing, transaction management, dialog management, and WebRTC session integration. It's been around since the early days of WebRTC and is well-understood.
SIP.js is a more actively maintained library with a cleaner API and better TypeScript support. It abstracts more of the complexity of coordinating SIP signaling with WebRTC media, making it the more common choice for new projects.
Both libraries handle the same fundamental task: they manage the SIP state machine (registrations, sessions, subscriptions) and coordinate with the browser's WebRTC API to generate SDP, handle ICE candidates, and manage the media session lifecycle. They send and receive SIP messages over a WebSocket connection and translate between SIP session events and WebRTC peer connection state.
There are also commercial SDKs from VoIP platform providers that wrap these open-source libraries with additional features like call queuing, presence, and analytics.
Session Border Controllers and WebRTC gateways
In practice, browser-based SIP clients rarely talk directly to a traditional SIP PBX. There's usually a WebRTC-aware component in the path, either a standalone WebRTC gateway or an SBC with WebRTC support built in.
This component does several things:
- Transport translation: Converts between SIP over WebSocket and SIP over UDP/TCP/TLS for communication with traditional SIP infrastructure.
- Media bridging: Terminates WebRTC media (ICE, DTLS-SRTP, Opus) on one side and establishes traditional RTP sessions on the other.
- Codec transcoding: Converts between Opus and G.711/G.729 when necessary.
- ICE handling: Participates in ICE negotiation with the browser, acting as the media endpoint that the browser's WebRTC engine connects to.
Major SBC vendors (AudioCodes, Oracle, Ribbon) all support WebRTC now. Open-source options like Oobitel with its built-in WebRTC support, or Okamailio with the WebSocket module, provide the same functionality. FreeSWITCH has had WebRTC support for years and is commonly deployed as a WebRTC gateway.
NAT traversal: how WebRTC changes everything
One of the most painful aspects of traditional SIP is NAT traversal. SIP embeds IP addresses inside message bodies and headers, and NAT devices don't rewrite those embedded addresses, leading to one-way audio, registration failures, and hours of troubleshooting.
WebRTC sidesteps this problem almost entirely by building ICE (Interactive Connectivity Establishment) into the core protocol. ICE is mandatory in WebRTC, not optional. Every WebRTC session uses it.
Here's how it works in the browser context:
- The browser gathers its local network interface addresses (host candidates).
- It queries a STUN server to discover its public-facing address (server-reflexive candidates).
- If configured, it allocates a relay address on a TURN server (relay candidates).
- All of these candidates are included in the SDP (or sent as trickle candidates).
- Both sides perform connectivity checks by sending STUN binding requests on each candidate pair.
- The pair that works best is selected for media.
Because ICE tests actual connectivity before sending media, it doesn't matter what NAT type is between the browser and the network. If direct connectivity works, ICE will find it. If it doesn't, ICE falls back to TURN relay. This is a fundamentally more robust approach than the traditional SIP methods of STUN-based address discovery or SIP ALGs, which guess at what might work based on incomplete information.
The result is that browser-based VoIP calls have far fewer NAT-related audio problems than traditional SIP deployments. The one common exception is corporate networks that block UDP entirely or restrict TURN traffic, which can prevent WebRTC media from flowing at all.
Quality considerations
WebRTC mandates the Opus codec, which operates at 48 kHz sample rate and supports both narrowband and wideband audio. Compared to the G.711 (narrowband, 8 kHz, 64 kbps) that dominates traditional VoIP, Opus sounds dramatically better. It's also far more bandwidth-efficient, the same audio quality can be achieved at lower bitrates, and it adapts dynamically to network conditions by adjusting bitrate, frame size, and complexity in real time.
This adaptability is one of WebRTC's under-appreciated strengths. When network conditions degrade, the Opus encoder reduces its bitrate before packets start getting dropped. WebRTC also includes built-in bandwidth estimation (REMB or Transport-CC) that continuously measures the available network capacity and feeds that information back to the encoder.
That said, browser-based VoIP introduces its own quality variables. The browser shares the network stack with everything else the user is doing. Background tabs downloading files, OS updates, other applications competing for bandwidth, all of these affect the WebRTC media stream. And WiFi adds latency and jitter that a wired desk phone on a properly configured VLAN would never see.
The codec advantage and the network disadvantage roughly cancel out for many deployments. Browser-based calls on a stable network sound better than desk phones. Browser-based calls on congested WiFi can sound worse.
Where this is headed
WebRTC has effectively won the browser and mobile client battle. Every major UCaaS and CCaaS platform (Owebex, Ooom, RingCentral, Ogenesys, Five9) uses WebRTC for browser-based and increasingly for native desktop and mobile clients too. The traditional SIP desk phone isn't disappearing from conference rooms and front desks anytime soon, but new endpoints are overwhelmingly WebRTC-based.
SIP over WebSockets remains the most common signaling mechanism for these WebRTC clients, though some platforms have moved to proprietary signaling over REST or gRPC. The SIP/WS + WebRTC combination has the advantage of interoperability. A browser client using SIP.js talking to a FreeSWITCH server can interoperate with any SIP-compliant PBX, trunk provider, or carrier. Proprietary signaling locks you into a single platform.
The trend is clear: the browser is becoming the default softphone, and SIP over WebSockets is the bridge that connects it to the existing telephone network.
For the traditional SIP fundamentals that underpin browser-based VoIP, see What SIP Actually Is and Your First Call Flow: INVITE to BYE. For the NAT traversal problems that WebRTC's ICE approach largely eliminates, see NAT Traversal: Why SIP and NAT Don't Get Along. For more on how SDP and codec negotiation work, see SDP and Media Negotiation. And if you're running browser-based softphones on WiFi, Wired vs. WiFi for VoIP covers the quality implications.
Frequently Asked Questions
How does SIP work in a web browser?+
Browsers cannot send raw UDP or TCP to arbitrary ports, so SIP messages are carried over a WebSocket (WSS) connection to a SIP server. The browser opens a secure WebSocket connection, then sends and receives standard SIP messages as WebSocket text frames. The media (actual audio) is handled separately by WebRTC.
What is the relationship between SIP and WebRTC?+
In browser-based VoIP, SIP over WebSockets handles the signaling (setting up, modifying, and tearing down calls), while WebRTC handles the media (encoding, encrypting, and transporting audio and video). SIP carries the call control, and the SDP within SIP messages describes the WebRTC media parameters including ICE candidates for NAT traversal.
Do browser-based VoIP calls sound as good as desk phones?+
Often better. WebRTC mandates the Opus codec, which is wideband and adapts dynamically to network conditions. Most desk phones use narrowband G.711 or G.729. The main quality variable for browser calls is the user's network and whether they are on WiFi, not the technology itself. Run a VoIP quality test to verify your network can support real-time media.
Share
Want to know when we publish new articles? Sign up for updates