fix: WHIP browser-publish silently never sends media behind Cloudflare

MTX_WEBRTCADDITIONALHOSTS (the ICE host candidate MediaMTX advertises
for WebRTC/WHIP) was wired to PUBLIC_HOST, the Cloudflare-proxied
domain. The WHIP HTTP handshake (SDP offer/answer through nginx) still
succeeds through Cloudflare, so 'stream from this browser' looks like
it works — but the actual media is a raw UDP path (port 8189) that
Cloudflare never forwards regardless of port, same as the earlier RTMP
issue. The browser ends up trying to send video/audio to Cloudflare's
edge, which drops it, so nothing ever actually arrives.

Split into a dedicated MEDIAMTX_WEBRTC_HOST env var (raw origin IP in
production) instead of reusing PUBLIC_HOST, mirroring how
MEDIAMTX_RTMP_PUBLIC already does this for the same reason. Confirmed
via podman inspect that the previously-deployed container really was
resolving MTX_WEBRTCADDITIONALHOSTS to the Cloudflare-proxied hostname
before this fix.
This commit is contained in:
2026-08-11 10:08:42 +00:00
parent 50d66fa2ea
commit 2f3a489a8a
3 changed files with 21 additions and 2 deletions
+8
View File
@@ -10,6 +10,14 @@ MEDIAMTX_WHIP_PUBLIC=http://${PUBLIC_HOST}:8889
MEDIAMTX_HLS_PUBLIC=http://${PUBLIC_HOST}:8890 MEDIAMTX_HLS_PUBLIC=http://${PUBLIC_HOST}:8890
BLOSSOM_URL_DEFAULT=http://${PUBLIC_HOST}:8098 BLOSSOM_URL_DEFAULT=http://${PUBLIC_HOST}:8098
# ICE host candidate MediaMTX advertises for WebRTC/WHIP (browser-publish
# "stream from this browser"). If you're behind Cloudflare or similar
# HTTP(S)-only proxy, this MUST be the raw origin IP, not PUBLIC_HOST —
# Cloudflare never forwards raw UDP, so a proxied hostname here makes the
# WHIP handshake succeed while media silently never arrives. Same reasoning
# as MEDIAMTX_RTMP_PUBLIC above. Plain host/IP, no scheme or port.
MEDIAMTX_WEBRTC_HOST=${PUBLIC_HOST}
# Default nostr relays for NIP-53 live-event announcements (comma separated, # Default nostr relays for NIP-53 live-event announcements (comma separated,
# changeable at runtime in Settings) # changeable at runtime in Settings)
NOSTR_RELAYS=wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band NOSTR_RELAYS=wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band
+10 -2
View File
@@ -44,8 +44,16 @@ services:
- "8189:8189/udp" # WebRTC ICE - "8189:8189/udp" # WebRTC ICE
- "8890:8888" # HLS (host 8890; 8888 kept free for other apps) - "8890:8888" # HLS (host 8890; 8888 kept free for other apps)
environment: environment:
# Browsers need a reachable ICE host candidate; set PUBLIC_HOST in .env # Browsers need a reachable ICE host candidate for the actual UDP media
MTX_WEBRTCADDITIONALHOSTS: ${PUBLIC_HOST:-localhost} # path (browser-publish "stream from this browser" / WHIP). This must
# be the raw origin IP, NOT PUBLIC_HOST — Cloudflare's proxy only
# forwards HTTP(S), never raw UDP, regardless of port (same reason
# MEDIAMTX_RTMP_PUBLIC above uses the raw IP instead of the
# Cloudflare-proxied domain). Using PUBLIC_HOST here means the browser
# resolves the ICE candidate to Cloudflare's edge and the WHIP HTTP
# handshake succeeds while media silently never arrives — set
# MEDIAMTX_WEBRTC_HOST in .env.
MTX_WEBRTCADDITIONALHOSTS: ${MEDIAMTX_WEBRTC_HOST:-localhost}
volumes: volumes:
- ./mediamtx/mediamtx.yml:/mediamtx.yml:ro - ./mediamtx/mediamtx.yml:/mediamtx.yml:ro
- mediamtx-recordings:/recordings - mediamtx-recordings:/recordings
+3
View File
@@ -39,6 +39,9 @@ webrtc: yes
webrtcAddress: :8889 webrtcAddress: :8889
webrtcLocalUDPAddress: :8189 webrtcLocalUDPAddress: :8189
webrtcAllowOrigins: ["*"] webrtcAllowOrigins: ["*"]
# webrtcAdditionalHosts is set via MTX_WEBRTCADDITIONALHOSTS in
# docker-compose.yml (MEDIAMTX_WEBRTC_HOST in .env) — see the comment there
# for why it must be the raw IP, not the Cloudflare-proxied domain.
# ---- recording ----------------------------------------------------------- # ---- recording -----------------------------------------------------------
pathDefaults: pathDefaults: