You test the page on your laptop, the player starts, and the ticket gets closed. The viewer on the other end opens the same page and gets a spinner that never resolves. The usual explanation offered is that the VPN is on the server, so playback should work — but the browser is the component asking for the playlist, and it has its own network path. A page embedded on a server with private access does not lend that access to whoever loads it.

This is about an existing ASP.NET page playing an HLS source such as http://10.5.5.9:8080/live/stream.m3u8, which is an illustrative private address. Two arrangements work; one of them is probably not what you built.

Myth: a loading page proves the media path

The HTML for that page comes from your web server, over whatever route your web server has. The playlist and the segments come from the media origin, over whatever route the requesting component has. When the player runs in the viewer's browser, those are two different clients, two different source addresses and often two different failure domains. Seeing the first succeed tells you nothing about the second.

What your test actually proved is narrower than it looked: your machine can reach the public page and the private media host at the same time. The viewer's machine was never part of the experiment.

What the playlist asks for next

RFC 8216 separates media playlists, which list segments, from master playlists, which list other playlists, and it resolves relative URIs against the URI of the containing playlist. That rule is the part people skip. Fetching one .m3u8 file successfully is not a playback test, because the file you fetched is an index, not the media.

Follow the chain the way the player does: open the selected playlist, then request the resources it names, then follow any redirect. A playlist served through a reachable web server can still point the viewer at an unreachable private address. Equally, moving a relative playlist to a different URL changes the base its entries resolve against, which is why rewriting the first visible URL and stopping there usually produces a valid-looking playlist that cannot play.

Direct viewing: the viewer needs the path

If viewers are supposed to reach the media host directly, their devices need the approved VPN or another private path, DNS that resolves the names you actually used, permission to reach those destinations, and whatever authentication the media service requires. Check each on the viewer's device rather than on the server. A browser on a phone with the VPN profile installed but not connected is a common and completely reproducible failure.

This arrangement keeps the source private and pushes the access requirement onto the audience. That is a policy decision as much as a technical one, so confirm that every intended viewer is allowed to hold that path before you design around it.

Server-side delivery: one controlled entry point

The alternative is an authorized media frontend that fetches the source over its private path and republishes only the viewer-facing resources. Keep the playlist, the segments and any key resources consistent with that single entry point, or the player will fetch a rewritten playlist over the frontend and the segments straight from an address the viewer cannot reach.

Constrain the source and the allowed paths in that frontend. A user-supplied arbitrary URL turns a helpful proxy into an unrestricted fetcher of your internal network, which is a much worse problem than a stream that will not play. Allow-list the origin and the path prefix instead of validating the string after the fact.

For an existing HLS source, first check whether controlled forwarding is enough; a VPN in the middle does not imply transcoding is required. Then validate the actual browser and codec combination, because a reachable response and a supported format are separate conditions. Keep the origin's TLS, authentication and browser security requirements intact — an embedded player is not an exemption from them.

Test the chain, not the URL

Run the checks from the component that will really make each request, and treat every step as its own result:

curl -s http://10.5.5.9:8080/live/stream.m3u8
curl -s -o /dev/null -w '%{http_code}\n' http://10.5.5.9:8080/live/segment-000123.ts

Then repeat it in the viewer's own browser with the intended stream, not a test pattern from your desk. Page load, playlist load, segment retrieval and an authorization failure are four distinct outcomes, and you want all four observed before calling it fixed. Do not install a legacy browser plugin because an old example played one stream that way; the missing piece is usually the viewer's path or the unsigned segment URL.

Once the arrangement is deliberate, write it down in one line: who fetches the media, over which path, and what the viewer is allowed to request. That sentence settles most future arguments about who the stream is really for.

ХӀокхунах тера йаззамаш