Blog
Engineering 2 min read

HLS won't play on iOS Safari? a checklist

iOS Safari plays HLS natively but breaks on hls.js, missing playsinline, wrong MIME type, or unmuted autoplay. A checklist of the usual causes and fixes.

rehelios

Engineering

On iPhone, HLS plays through Safari’s native <video> element — not through hls.js, which the iOS Media Source path doesn’t support for HLS. Most breakage comes from four things: forcing hls.js on iOS, a missing playsinline attribute, the wrong MIME type on the .m3u8, or an autoplay that isn’t muted. Serve application/vnd.apple.mpegurl, add playsinline, and let Safari play the URL directly.

The core gotcha: don’t use hls.js on iOS

hls.js is the right tool almost everywhere — except iPhone. iOS Safari doesn’t expose Media Source Extensions for HLS, so hls.js can’t attach its SourceBuffer there. What it can do is play HLS natively: hand the .m3u8 straight to a <video> element and Safari handles adaptive streaming itself. The standard pattern is to feature-detect and let native win:

if (video.canPlayType('application/vnd.apple.mpegurl')) {
  video.src = playlistUrl;            // iOS Safari: native HLS
} else if (Hls.isSupported()) {
  new Hls().loadSource(playlistUrl);  // everyone else: hls.js
}

If you skip the check and always call hls.js, iPhones get a black player.

The checklist

Serve the right MIME type

The .m3u8 must be sent as application/vnd.apple.mpegurl (or application/x-mpegURL). A generic text/plain makes Safari refuse it.

Add playsinline

Without it, iPhone forces fullscreen playback and inline autoplay silently fails. Pair it with muted if you autoplay.

Muted for autoplay

iOS only autoplays muted, inline video. An unmuted autoplay is blocked outright — require a tap, or start muted.

HTTPS, CORS, and Range

Serve over HTTPS, and make sure segments answer Range requests — Safari relies on byte-range seeking. (See the hls.js CORS fix.)

The subtler ones

  • Codec support. iOS is strict: H.264/AAC in an HLS playlist is safe. An HEVC or AV1 rendition without an H.264 fallback rung can leave iPhone with nothing to play even though the manifest loads.
  • Signed URL expiry mid-session. Safari re-requests the playlist during long sessions; if your signed token expires between requests, playback stops. Tokens need a lifetime longer than the video, or a renewal scheme — the reason token renewal matters is covered in how signed playback tokens work.
  • Low Power Mode. iOS blocks autoplay entirely in Low Power Mode — always give users a tap-to-play control as the fallback.

The short version

On iPhone, get out of hls.js’s way and let Safari play the URL: correct MIME type, playsinline, muted for autoplay, HTTPS with Range support. Do that and native HLS is rock-solid.

rehelios emits standards-compliant HLS with an H.264 base rung and the correct MIME type, so it plays natively on iOS out of the box — and MPEG-DASH alongside it for everywhere else. More on the formats in HLS vs DASH, and the delivery setup in the docs.

Put your first video live today

Create an account, upload a file, and have a fast, embeddable video live in minutes. Pay only for what you store and stream.