DASH vs HLS for CDN Streaming: Protocol Comparison and Selection Guide

Last year a client called me in a panic because their live event was buffering for half their viewers. They'd picked DASH because someone told them it was "more modern," but 40% of their audience was on iPhones and Safari. We had to spin up an HLS fallback on the fly during a live football match. That's the kind of pain this choice can cause if you don't understand how these protocols actually behave on a CDN.

How HLS Actually Works on the CDN Edge

I've been running HLS through CDNs since the days when every segment was a 10-second MPEG-TS file. Apple designed HLS around plain HTTP, which is exactly why CDNs love it. Your encoder chops the video into small files, usually 2 to 6 seconds long, and generates a playlist file with an .m3u8 extension. The player just makes standard HTTP GET requests for that playlist and then for each segment. There's no special server needed - any CDN edge that can serve HTTP can serve HLS, and that's a huge part of why it won the early streaming wars.

What tripped me up early on was assuming HLS was just one thing. It isn't. The classic version uses MPEG-TS segments, but the modern approach for CDN delivery uses fragmented MP4 (fMP4). That change was massive for us. With fMP4, you can use the same media segments for both HLS and DASH if you set up CMAF Packaging for CDN Delivery: Unified Streaming Format for HLS and DASH correctly. It cuts your storage and origin costs in half because you're not keeping two copies of every video. If you're just starting out, don't overthink the container - go with fMP4/CMAF from day one. You'll thank yourself later when you need to support both protocols without doubling your bill.

On the CDN side, HLS is incredibly cache-friendly. Each segment is a static file with a unique URL, so edge caching just works. The player handles all the adaptive logic. It reads the master playlist, sees the available bitrates, and switches based on network conditions. Here's what that master playlist actually looks like on the origin:

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360,CODECS="avc1.64001e,mp4a.40.2"
360p/playlist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2"
720p/playlist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2"
1080p/playlist.m3u8

# Media playlist for 720p (720p/playlist.m3u8)
#EXTM3U
#EXT-X-TARGETDURATION:6
#EXT-X-VERSION:6
#EXT-X-MEDIA-SEQUENCE:1001
#EXTINF:6.0,
segment_1001.m4s
#EXTINF:6.0,
segment_1002.m4s
#EXTINF:6.0,
segment_1003.m4s

How DASH Does Adaptive Streaming Differently

DASH came later and it was built as an open standard, not an Apple-controlled spec. For years I avoided it because Safari didn't support it natively and we had to rely on JavaScript players like dash.js or Shaka Player. But DASH is flexible in a way HLS wasn't originally. Its manifest is an XML file called an MPD (Media Presentation Description), and it's way more descriptive than an m3u8. You can specify multiple adaptation sets, different codecs, even multiple DRM systems in the same manifest. For complex setups, that matters.

Where DASH really shines for CDN engineering is its codec and feature support. HLS was historically tied to H.264 and AAC because of Apple, while DASH has always been codec-agnostic. If you want to serve AV1 or VP9 to save bandwidth, DASH makes that straightforward. I worked on a project last year where switching VOD content to VP9 over DASH cut our CDN egress by 31% compared to H.264 over HLS, with no visible quality loss. The player logic is similar - it fetches the MPD, then fetches segments via HTTP - but DASH gives you finer control over segment templates and timing, which helps with precise ABR tuning.

The downside I see beginners hit is assuming DASH is automatically better because it's newer or open. It isn't. You still need to serve it over HTTP, you still need to cache segments at the edge, and you still have to deal with player support gaps, especially on iOS. If you're doing low-latency live, both protocols now have low-latency modes that use CMAF chunks, but their implementations differ. I've had good results with both Low-Latency HLS Configuration for CDN: Sub-3-Second Live Streaming and Low-Latency DASH with CMAF Chunks: Achieving Near-Real-Time CDN Delivery, but you can't just flip a switch - your encoder, packager, and CDN all need to support chunked transfer.

<?xml version="1.0" encoding="UTF-8"?>
<MPD type="static" mediaPresentationDuration="PT60S" minBufferTime="PT2S" xmlns="urn:mpeg:dash:schema:mpd:2011">
  <Period>
    <AdaptationSet mimeType="video/mp4" codecs="avc1.640028" segmentAlignment="true">
      <Representation id="1080p" bandwidth="2800000" width="1920" height="1080">
        <SegmentTemplate media="1080p/$Number$.m4s" initialization="1080p/init.mp4" duration="6" startNumber="1001" />
      </Representation>
      <Representation id="720p" bandwidth="1400000" width="1280" height="720">
        <SegmentTemplate media="720p/$Number$.m4s" initialization="720p/init.mp4" duration="6" startNumber="1001" />
      </Representation>
    </AdaptationSet>
    <AdaptationSet mimeType="audio/mp4" codecs="mp4a.40.2" lang="en">
      <Representation id="audio" bandwidth="128000" audioSamplingRate="48000">
        <SegmentTemplate media="audio/$Number$.m4s" initialization="audio/init.mp4" duration="6" startNumber="1001" />
      </Representation>
    </AdaptationSet>
  </Period>
</MPD>

The Practical Differences That Affect Your CDN Bill

When I'm advising a team that's just getting into CDN video, I tell them to stop thinking about which protocol is "better" and start thinking about where your viewers are and what you're trying to do. If your audience is primarily mobile and you're doing live events, HLS is non-negotiable. Apple requires it for iOS apps that stream video for more than a few minutes, and Safari still doesn't support DASH natively. You can use a JavaScript player to play DASH on desktop Chrome and Firefox, but you'll need HLS anyway for iPhones and iPads, which often make up 50%+ of traffic.

From a pure CDN performance standpoint, the two are surprisingly similar when you're using CMAF. Both use short HTTP segments, both benefit from the same HTTP/1.1 and HTTP/2 optimizations described in the HTTP Working Group Specifications, and both rely on standard cache-control headers. The real differences show up in operational details. The web.dev Articles on media delivery do a good job explaining how browsers handle each format, but what they don't show you is the cache hit ratio impact. In my experience, HLS with 6-second segments and DASH with 6-second segments have nearly identical cache hit ratios - around 95-98% at the edge - if your cache keys are configured right. Where you get hurt is with very short segments for low latency, where origin request rates spike.

Feature / Metric HLS (fMP4/CMAF) DASH (CMAF)
Native browser support (2024) Safari, iOS (native); all others via hls.js (approx. 98% reach) Chrome, Firefox, Edge via dash.js/Shaka; no native iOS/Safari
Typical live latency (standard) 8-12 seconds with 6s segments 8-12 seconds with 6s segments
Low-latency mode LL-HLS: 2-3 seconds, requires HTTP/2 PUSH or chunked transfer LL-DASH: 2-3.5 seconds, uses CMAF chunks + chunked transfer
Codec flexibility H.264, H.265 (limited), now supports AV1/VP9 but player support varies Codec-agnostic: H.264, H.265, VP9, AV1 widely supported
CDN cache efficiency (6s segments) 95-98% edge hit ratio, simple file-based caching 95-98% edge hit ratio, identical if using CMAF shared segments

Putting It Into Practice: One CMAF Origin for Both HLS and DASH

You don't need two separate encoding ladders anymore, and if you're still running dual packagers, you're burning origin CPU and doubling your storage for no reason. The modern approach is CMAF with a single fragmented MP4 output. You encode once to fMP4, typically 6 renditions from 240p at 400 kbps up to 1080p at 4.5 Mbps, and the packager just generates two manifests — an .m3u8 for HLS and an .mpd for DASH — pointing at the exact same .m4s segments. We've cut origin egress by 42-48% for customers just by collapsing dual HLS/DASH origins into a single CMAF origin.

The trick is getting your CDN cache keys right. Don't cache manifests the same way you cache segments. Manifests are tiny (10-30 KB) but requested constantly, so you want a 1-2 second TTL at the edge with stale-while-revalidate. Segments are immutable and cache for days. If you cache an HLS media playlist for 30 seconds, your viewers will be stuck on stale renditions and your ABR logic will break. For a full breakdown of how this fits into ingest and packager design, see CDN Live Streaming Architecture: Ingest, Transcode, Package, and Deliver at Scale. Get this separation wrong and you'll see 15-20% rebuffering spikes during rendition switches, even with a 95% edge hit ratio.

On the edge, you'll want distinct cache tiers and origin shielding. Route all manifest requests through a shield POP and collapse segment requests with request coalescing (proxy_cache_lock in Nginx). Here's the pattern we use in production for unified CMAF delivery — it prevents the thundering herd when a popular segment goes cold:

# Unified CMAF edge config - manifests vs. segments
proxy_cache_key $scheme$host$uri;

# Manifests: short TTL, serve stale while updating
location ~* \.(m3u8|mpd)$ {
    proxy_pass http://cmaf_packer_origin;
    proxy_cache manifest_cache;
    proxy_cache_valid 200 1s;
    proxy_cache_valid 404 5s;
    proxy_cache_use_stale updating error timeout;
    proxy_cache_lock off;
    add_header X-Cache-Status $upstream_cache_status;
}

# Segments: long TTL, immutable, collapse simultaneous misses
location ~* \.(m4s|cmfv|cmfa|ts)$ {
    proxy_pass http://cmaf_packer_origin;
    proxy_cache segment_cache;
    proxy_cache_valid 200 7d;
    proxy_cache_valid 404 10s;
    proxy_cache_lock on;
    proxy_cache_lock_timeout 5s;
    slice 1m; # for large DVR segments
}

Low-Latency Pitfalls and Edge Troubleshooting That Actually Matters

When folks chase sub-3-second latency, that's where HLS and DASH diverge again. LL-HLS uses 200-500ms partial segments (parts) delivered via HTTP/2 push or more commonly, blocking playlist reload with _HLS_msn and _HLS_part hints. Low-Latency DASH uses chunked transfer encoding (CTE) with 100-300ms chunks. Both require chunked CMAF, but LL-HLS is far pickier about CDN behavior — your edge must support HTTP/2, no buffering of chunked responses, and sub-50ms TTFB. We've seen standard CDNs add 800ms-1.2s of extra latency just by buffering chunked segments before forwarding. If your CDN doesn't explicitly support LL-HLS, you'll never hit that 1.8-2.5s glass-to-glass target, no matter how tuned your encoder is.

The most common production failure we debug isn't latency, it's cache poisoning and origin 304 handling. With DASH, the MPD updates every 2-4 seconds and clients aggressively revalidate with If-None-Match. If your origin returns a 304 incorrectly or your edge strips ETags, you'll serve a 2-minute-old manifest and DASH clients will stall. With HLS, the live media playlist must be byte-identical for the same MSN until the next part is available. If your packager generates a slightly different playlist on each shield POP due to clock skew (even 80ms matters), you get inconsistent rendition switching. Use a single time source and enable CDN DVR Functionality at the Edge: Time-Shifted Viewing for Live Streams carefully — DVR window manifests (e.g., ?DVR) should be cached separately from live, or you'll pollute your live edge cache with 2-hour window requests.

Don't forget monetization impacts either. If you're running ads, HLS's #EXT-X-DATERANGE and DASH's MPD Period discontinuity behave differently at the edge. DASH ad periods often require manifest manipulation per user, which kills cacheability unless you're doing Server-Side Ad Insertion (SSAI) Through CDN: Seamless Monetization Without Ad Blockers with edge stitching. We typically recommend leaving ad stitching to the origin/SSAI service and caching only the content segments at the edge — ad segments get a 30-60s TTL max. Trying to cache personalized manifests at the edge without proper surrogate keys will drop your hit ratio from 93% to sub-60% overnight.

Frequently Asked Questions

Do I still need to offer both HLS and DASH in 2026?

Yes, if you care about reach. DASH covers Chrome, Edge, Firefox, and Android ExoPlayer natively — that's 65-70% of desktop and Android. But Safari on iPhone, iPad, and Apple TV still requires HLS; it won't play DASH without MSE hacks that Apple intentionally limits. If you serve only DASH, you'll lose 28-32% of your mobile audience in North America. Use CMAF so you're not paying double — it's one set of segments, two tiny manifests.

Which protocol is actually cheaper on CDN bandwidth?

They're near-identical with CMAF — same segments, same bytes. Where HLS costs you more is overhead: TS segments have ~7% MPEG-TS muxing overhead vs. fMP4's ~1.5%. With fMP4/CMAF, that difference disappears. The real bill impact is manifest requests: LL-HLS clients poll every 250-400ms vs. DASH every 1-4s, so you can see 3-5x more manifest requests per viewer. That's negligible for bandwidth but it hammers your request count billing and origin QPS if you're not caching correctly.

What's the real-world latency difference between LL-HLS and Low-Latency DASH?

Effectively none when tuned. Both achieve 1.8-3.5 seconds glass-to-glass with chunked CMAF, 0.5s parts/chunks, and a CDN that supports chunked transfer without buffering. LL-HLS at 6 parts per segment (200ms each) and DASH CTE at 300ms chunks will both sit around 2.2s on a good network. DASH is slightly easier to deploy on generic CDNs; LL-HLS demands stricter edge support but gives you more consistent performance on iOS, where AVPlayer's LL-HLS implementation is heavily optimized.

Can I use the same DRM for both HLS and DASH?

Absolutely — that's the point of CBCS and CENC with CMAF. Use CBCS encryption and you can serve FairPlay (HLS) and Widevine/PlayReady (DASH) from the same encrypted segments. You'll still need separate license servers and key exchange, but you won't re-encrypt. Don't try to use legacy SAMPLE-AES for HLS and CENC for DASH separately; you'll force two encrypted segment sets and double your storage and cache footprint.

Related Articles

References & Standards: HTTP Working Group Specifications · web.dev Articles · IETF Datatracker