Home / Guides / Why an m3u8 URL Won't Play in Chrome

Why an m3u8 URL Won't Play in Chrome

Desktop Chrome does not play HLS natively. Pasting an .m3u8 URL into the address bar, or setting it as a <video src>, will do nothing. Safari on macOS and iOS, and most Android browsers, support HLS natively; Chrome on the desktop needs a JavaScript player such as hls.js, which feeds the segments into the browser through Media Source Extensions (MSE). This single difference explains most "works on my iPhone, broken on Chrome" reports.

The smallest working example

<video id="v" controls muted style="width:100%"></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<script>
  const url  = 'https://your-domain/hls/index.m3u8';
  const v    = document.getElementById('v');
  if (v.canPlayType('application/vnd.apple.mpegurl')) {
    v.src = url;                                  // native HLS (Safari, iOS)
  } else if (Hls.isSupported()) {
    const hls = new Hls();
    hls.loadSource(url);
    hls.attachMedia(v);
    hls.on(Hls.Events.ERROR, (_, data) => {
      if (data.fatal) console.error(data.type, data.details);
    });
  } else {
    console.error('HLS is not supported in this browser');
  }
</script>

The canPlayType branch is important: on Safari you should use native playback rather than MSE, and on browsers without MSE at all you need a graceful fallback. Pin hls.js to a specific version in production instead of floating @1.

Diagnose in the right order

Open DevTools and check the Network tab before blaming the player code. You are looking for three separate requests: the master playlist, the variant playlist, and the first segment.

Other frequent causes

A quick decision tree

If curl returns a valid playlist and a valid segment, the stream itself is fine and the problem is in the browser layer: player library, CORS, MIME types, or autoplay. If curl fails, fix the server first and do not touch the JavaScript. Testing the same URL in Safari is a useful second data point: when Safari plays it and Chrome does not, it is almost always one of the browser-layer issues above rather than the encode.

Related reading