StreamTest

How to test an HLS stream (free HLS stream tester)

To test an HLS stream, fetch its .m3u8 URL over HTTPS and confirm it returns a valid master or media playlist, then fetch the first few segments it references and confirm they play. A working test also checks CORS headers, TLS certificate validity, and whether the playlist updates on a live stream, since any one of those can break playback even when the manifest text looks fine.

Most "stream not working" reports come from one of four places: the manifest itself (missing tags, wrong MIME type), the segment files (404s, codec mismatch), the server (missing CORS, expired cert), or the player (unsupported feature). A proper test walks through each layer in that order rather than guessing.

What does a valid .m3u8 response actually look like?

A valid response starts with the literal line #EXTM3U as the first line of the body, with no BOM or whitespace before it. The Content-Type header should be application/vnd.apple.mpegurl or application/x-mpegURL; audio/mpegurl and text/plain both work in most players but fail strict validators and some Smart TV stacks.

A master playlist lists #EXT-X-STREAM-INF lines, each followed by a URI to a variant media playlist, and each STREAM-INF line should carry BANDWIDTH and, ideally, RESOLUTION and CODECS. A media playlist instead lists #EXTINF durations followed by segment URIs, and must declare #EXT-X-VERSION and #EXT-X-TARGETDURATION near the top.

Why does the playlist load but the video never starts?

This is almost always a segment-fetch failure, a CORS block, or a codec the player cannot decode. Check each segment URI individually: if they are relative, resolve them against the playlist URL, not the page URL, since a wrong base is a common source of 404s on CDN-hosted manifests.

If segments 200 in a plain fetch but the player still stalls, check the Access-Control-Allow-Origin header on both the playlist and the segments. Browsers require CORS on both, and a stream that CORS-permits the manifest but not the .ts or .m4s segments will pass a manual curl test yet fail silently in-browser.

If CORS and fetches are clean, check CODECS in the STREAM-INF line against what the segments actually contain. A declared codec of hvc1 (HEVC) will not play in Safari on older iOS or in most desktop Chrome without hardware support, even though the manifest and segments are both technically valid.

How do you tell a live stream from a VOD stream from the manifest alone?

A VOD or fully-buffered playlist ends with #EXT-X-ENDLIST. If that tag is absent, treat the stream as live or event-type and expect the client to re-fetch the media playlist on an interval, typically close to the segment target duration.

A live playlist should also carry #EXT-X-PLAYLIST-TYPE:EVENT or omit PLAYLIST-TYPE entirely for a sliding window; EVENT means segments only ever get appended, VOD means the full list is fixed. Testing a live stream once and caching the result is meaningless, since the playlist content is expected to change on every poll.

What HTTP response codes and headers should the test check?

The manifest request should return 200, not a redirect chain longer than two hops, since some players cap redirect follows. Segment requests should also return 200; a 403 on segments while the manifest 200s usually means a signed-URL or token has expired between the manifest fetch and the segment fetch, which points at a token TTL that is too short relative to segment count.

Check Cache-Control on the live media playlist: it should be short or no-cache (a few seconds at most), because a CDN or browser caching a live playlist for minutes will serve stale segment lists and the player will appear frozen even though the origin is updating correctly.

What does a broken TARGETDURATION or segment count reveal?

EXT-X-TARGETDURATION must be an integer at least as large as the longest EXTINF duration in the playlist; if any segment exceeds it, strict clients and Apple's own validator will reject the playlist. A TARGETDURATION much larger than the actual segment durations is not invalid but signals the encoder is being conservative, which increases end-to-end latency because clients poll less often.

For a live sliding window, the spec expects roughly three segments minimum, though most real deployments keep four to six. Fewer than three leaves too little buffer for network jitter; a client that briefly falls behind runs out of playlist and stalls.

How do you test HLS on a page that already has strict CSP or mixed content rules?

If the page is served over HTTPS, the manifest and every segment URL must also be HTTPS, or the browser silently blocks the request as mixed content with no player-level error surfaced. Check the browser console, not just the player's error callback, since mixed-content blocks often do not reach hls.js or Shaka's error handlers at all.

A Content-Security-Policy with a restrictive connect-src or media-src will block XHR-based HLS players (hls.js, Shaka) even when native <video> playback would work, because native HLS in Safari uses the media engine rather than fetch/XHR. Testing in Safari alone can therefore hide a CSP problem that breaks every other browser.

Step by step

  1. Fetch the .m3u8 URL directly and confirm the first line is #EXTM3U with a 200 status.
  2. Check the Content-Type header is a recognised HLS MIME type.
  3. If it is a master playlist, open each STREAM-INF variant and confirm its URI resolves and returns a valid media playlist.
  4. Pick the first two or three segment URIs from the media playlist and fetch them directly to confirm 200 status and non-zero size.
  5. Check CORS headers (Access-Control-Allow-Origin) on both the playlist and the segments if the stream will be played from a browser.
  6. For a live stream, re-fetch the media playlist after roughly one target duration and confirm the media sequence number has advanced.
  7. Load the URL in an actual player (hls.js, Shaka, or native Safari) and confirm the first frame renders within a few seconds.

Run it yourself

Free, no sign-up, and every finding links to an explainer.

Frequently asked questions

Can I test an HLS stream without a player, just from the manifest?
Yes, up to a point: fetching and parsing the manifest and segments catches most structural problems (missing tags, bad URIs, wrong TARGETDURATION, expired tokens). It cannot catch codec-support or DRM issues that only surface once a real decoder tries to play the segments, so a manifest-only check is a first pass, not a substitute for playback.
Why does the stream play in VLC but not in a browser?
VLC uses its own demuxer and ignores CORS entirely, so a stream that fails in-browser due to missing Access-Control-Allow-Origin headers will play fine in VLC. It can also decode codecs, like certain HEVC profiles, that browsers do not support natively.
What is a reasonable time-to-first-frame for HLS?
For a VOD stream with a CDN-hosted first segment, one to three seconds is typical. For a live stream, add roughly one to two segment durations on top of that, since the player generally needs to buffer at least one full segment before it can start decoding.
Does an HLS stream need a master playlist to be valid?
No. A single media playlist referenced directly is a valid HLS stream and plays in every major client; a master playlist is only required when you are offering multiple bitrate or audio/subtitle variants for the player to choose between.
My segments are .m4s files, not .ts — is that still HLS?
Yes, fMP4 (.m4s/.mp4) segments are a standard HLS delivery format alongside MPEG-TS, and are required for HEVC, and for fragmented content shared between HLS and DASH. The media playlist for fMP4 segments needs an #EXT-X-MAP tag pointing at the initialization segment before the first EXTINF.

HLS findings, explained

Read next

Last reviewed 2026-09-16.