WHEP (WebRTC-HTTP Egress Protocol)

Watching a WebRTC stream traditionally means building custom signaling for every player. WHEP (WebRTC-HTTP Egress Protocol) standardizes the playback side using standard HTTP Methods, where a viewer sends an SDP offer via POST, receives an SDP answer and a session URL, and ends playback with DELETE. It is the egress counterpart to WHIP, which handles ingestion.

Draft specification

WHEP is defined by the IETF WISH working group Internet-Draft draft-ietf-wish-whep, intended as a Proposed Standard. The latest revision has expired and the working group has it under revision, so specific details may shift before publication as an RFC. The protocol is already widely implemented. Track the current revision through the link in See also.

Usage

WHEP standardizes the signaling for media egress, the path from a media server to a viewer. WebRTC supplies the transport (ICE, DTLS, SRTP) but leaves signaling undefined, so every player integration historically built its own SDP exchange over WebSocket or a vendor API. That fragmentation blocked interoperability between streaming services and the devices playing their content.

A WHEP player sends an SDP offer via HTTP POST, receives an SDP answer in the Response, and begins Streaming with sub-second latency. The session lifecycle maps to standard HTTP semantics, so it works with existing load balancers, CDN infrastructure, and Authentication proxies. The same approach makes WHEP suitable for devices that run no custom JavaScript, such as smart TVs and set-top boxes.

WHEP mirrors the design of WHIP for the opposite direction. WHIP ingests media from an encoder into a server. WHEP delivers media from a server to a player. The two share the same HTTP method flow, the same Link header mechanism for ICE servers, and the same bearer-token authentication, which lets a single platform expose both ingest and playback over one consistent API.

Egress only

WHEP handles the egress direction (server to viewer). The ingest direction (encoder to server) is addressed by WHIP, a companion protocol from the same IETF working group, published as an RFC.

How WHEP works

Session creation

The WHEP player sends an HTTP POST to the WHEP endpoint URL with an SDP offer in the Request body. The Content-Type is application/sdp.

POST /whep/endpoint HTTP/1.1
Host: whep.example.re
Authorization: Bearer eyJhbGciOiJIUz...
Content-Type: application/sdp

v=0
o=- 5228595038118931041 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=ice-options:trickle ice2
m=audio 9 UDP/TLS/RTP/SAVPF 111
a=mid:0
a=recvonly
a=rtcp-mux-only
a=rtpmap:111 opus/48000/2
a=ice-ufrag:EsAw
a=ice-pwd:bP+XJMM09aR8AiX1jdukzR6Y
a=fingerprint:sha-256 DA:7B:57:DC:28:CE:04...
a=setup:actpass
m=video 9 UDP/TLS/RTP/SAVPF 96
a=mid:1
a=recvonly
a=rtcp-mux-only
a=rtpmap:96 VP8/90000
a=ice-ufrag:EsAw
a=ice-pwd:bP+XJMM09aR8AiX1jdukzR6Y
a=fingerprint:sha-256 DA:7B:57:DC:28:CE:04...
a=setup:actpass

The SDP offer uses a=recvonly, meaning the player receives media rather than sends it. This is the main distinction from WHIP, where the encoder offers a=sendonly. A WHEP player may offer a=sendrecv when it also returns audio or video, though playback-only viewers use recvonly.

The server responds with 201 Created, an SDP answer in the body, and a Location header pointing to the newly created session resource:

HTTP/1.1 201 Created
Content-Type: application/sdp
Location: https://whep.example.re/session/abc123
ETag: "xyzzy"
Link: <stun:stun.example.re>; rel="ice-server"
Link: <turn:turn.example.re?transport=udp>; rel="ice-server"; username="user"; credential="pass"

v=0
o=- 1657793490019 1 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=ice-lite
m=audio 9 UDP/TLS/RTP/SAVPF 111
a=mid:0
a=sendonly
a=rtcp-mux-only
a=rtpmap:111 opus/48000/2
a=ice-ufrag:38sdf4fdsf54
a=ice-pwd:2e13dde17c1cb009202f627fab90cbec
a=candidate:1 1 UDP 2130706431
  198.51.100.1 39132 typ host
a=fingerprint:sha-256 F7:EB:F3:3E:AC:D2...
a=setup:passive
m=video 9 UDP/TLS/RTP/SAVPF 96
a=mid:1
a=sendonly
a=rtcp-mux-only
a=rtpmap:96 VP8/90000
a=ice-ufrag:38sdf4fdsf54
a=ice-pwd:2e13dde17c1cb009202f627fab90cbec
a=candidate:1 1 UDP 2130706431
  198.51.100.1 39132 typ host
a=fingerprint:sha-256 F7:EB:F3:3E:AC:D2...
a=setup:passive

The Location header URL becomes the WHEP session URL. All subsequent operations (Trickle ICE, ICE restart, termination) target this URL. The ETag header identifies the current ICE session for conditional updates and is present when the server supports ICE restarts.

Link headers with rel="ice-server" provide STUN and TURN server configuration. TURN entries carry username and credential attributes for authentication.

Server-offered playback

WHEP also supports a mode where the server provides the SDP offer. When a player POSTs an offer the server cannot satisfy, the server returns 406 Not Acceptable and supplies its own SDP offer instead. The player then processes that offer and returns an answer. A compliant WHEP player supports both receiving an SDP answer to its own offer and receiving an SDP offer from the server.

Trickle ICE

After receiving the 201 response, the player sends newly gathered ICE candidates to the server via HTTP PATCH on the session URL. The Content-Type is application/trickle-ice-sdpfrag.

PATCH /session/abc123 HTTP/1.1
Host: whep.example.re
If-Match: "xyzzy"
Content-Type: application/trickle-ice-sdpfrag

a=ice-ufrag:EsAw
a=ice-pwd:bP+XJMM09aR8AiX1jdukzR6Y
m=audio 9 RTP/AVP 0
a=mid:0
a=candidate:1 1 UDP 2130706431
  203.0.113.1 9 typ host
a=end-of-candidates

The If-Match header carries the ETag from the 201 response, binding the update to the correct ICE session. The server returns 204 No Content on success.

Players buffer candidates gathered before the 201 response arrives and send them in a single aggregated PATCH. The a=end-of-candidates attribute signals gathering is complete.

ICE restart

When connectivity degrades, the player requests an ICE restart by sending a PATCH with If-Match: "*" (a wildcard indicating a restart rather than a regular candidate update):

PATCH /session/abc123 HTTP/1.1
Host: whep.example.re
If-Match: "*"
Content-Type: application/trickle-ice-sdpfrag

a=ice-ufrag:ysXw
a=ice-pwd:vw5LmwG4y/e6dPP/zAP9Gp5k
m=audio 9 RTP/AVP 0
a=mid:0
a=candidate:1 1 UDP 2130706431
  203.0.113.1 9 typ host

The server responds with 200 and a new SDP fragment containing fresh ICE credentials and candidates. A new ETag identifies the restarted session. Both sides discard old candidates and use the new set exclusively.

Session termination

The player ends playback with HTTP DELETE on the session URL:

DELETE /session/abc123 HTTP/1.1
Host: whep.example.re
HTTP/1.1 200 OK

This removes the WHEP session and frees all server resources, terminating the ICE and DTLS sessions.

Headers

WHEP uses standard HTTP headers throughout:

Header Direction Purpose
Content-Type Both application/sdp or application/trickle-ice-sdpfrag
Location Response WHEP session URL (201 response)
ETag Response ICE session identifier
If-Match Request ETag validation on PATCH
Authorization Request Bearer token for Authentication
Link Response STUN/TURN servers (rel="ice-server") and extensions
Accept-Post Response application/sdp (on OPTIONS)
Retry-After Response Wait time when server returns 503

Status codes

Code Context
201 Created Session created, SDP answer returned
200 OK ICE restart response or session deleted
204 No Content Trickle ICE candidates accepted
307 Temporary Redirect Load balancing to another server
400 Bad Request Malformed SDP
406 Not Acceptable Server returns its own SDP offer
412 Precondition Failed ETag mismatch
422 Unprocessable Content Trickle ICE or ICE restart not supported
503 Service Unavailable Server overloaded

WHEP endpoints use 307 for redirects (not 301 or 302) to preserve the POST method and body across the redirect.

Authentication

Bearer token Authentication is the mandatory-to-implement mechanism. The token is sent in the Authorization header on every Request except CORS preflight:

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Token distribution is out of scope for the protocol. Platforms provide the WHEP endpoint URL and a token through their dashboard or a content entitlement service. Tokens are JWTs, shared secrets, or URL-embedded values depending on the platform. HTTPS is required for all WHEP communication to preserve the WebRTC security model.

Extensions

WHEP supports protocol extensions discovered through Link headers in the 201 response. Extension rel values use the urn:ietf:params:whep:ext namespace, and IANA maintains a registry of WHEP extension URNs. Common extensions cover server-sent events for stream status, video layer selection for bandwidth adaptation, and playback control for time-shifted content. Players ignore unknown extensions, and servers do not require extensions for basic playback.

Example

A complete WHEP playback session: starting playback, sending ICE candidates, and stopping.

1. Start playback

POST /whep/live HTTP/1.1
Host: whep.example.re
Authorization: Bearer viewer-token-abc123
Content-Type: application/sdp

v=0
o=- 0 0 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0
a=ice-options:trickle
m=video 9 UDP/TLS/RTP/SAVPF 96
a=mid:0
a=recvonly
a=rtcp-mux-only
a=rtpmap:96 H264/90000
a=ice-ufrag:abcd
a=ice-pwd:efghijklmnopqrstuvwx
a=fingerprint:sha-256 AA:BB:CC:DD:EE:FF...
a=setup:actpass
HTTP/1.1 201 Created
Location: https://whep.example.re/session/s7k2m
ETag: "v1"
Content-Type: application/sdp
Link: <stun:stun.example.re>; rel="ice-server"

v=0
o=- 1 1 IN IP4 198.51.100.1
s=-
t=0 0
a=group:BUNDLE 0
a=ice-lite
m=video 9 UDP/TLS/RTP/SAVPF 96
a=mid:0
a=sendonly
a=rtcp-mux-only
a=rtpmap:96 H264/90000
a=ice-ufrag:wxyz
a=ice-pwd:1234567890abcdef
a=candidate:1 1 UDP 2130706431
  198.51.100.1 10000 typ host
a=fingerprint:sha-256 11:22:33:44:55:66...
a=setup:passive

2. Send ICE candidates

PATCH /session/s7k2m HTTP/1.1
Host: whep.example.re
If-Match: "v1"
Content-Type: application/trickle-ice-sdpfrag

a=ice-ufrag:abcd
a=ice-pwd:efghijklmnopqrstuvwx
m=video 9 RTP/AVP 0
a=mid:0
a=candidate:1 1 UDP 2130706431
  203.0.113.5 50000 typ host
a=end-of-candidates
HTTP/1.1 204 No Content

3. Media flows via WebRTC (ICE, DTLS, SRTP)

4. Stop playback

DELETE /session/s7k2m HTTP/1.1
Host: whep.example.re
HTTP/1.1 200 OK

The player (a browser, a smart TV app, or a hardware device) receives live media from the server for the duration of the session. The matching ingest side, where an encoder feeds the server, is handled by WHIP.

See also

Last updated: June 5, 2026