Video Streaming CDN Architecture: HLS, DASH and Adaptive Bitrate Delivery

Delivering video at scale is fundamentally different from delivering images, scripts, or API responses. A single 90-minute live event can generate more egress than your entire static asset catalog for a month, and the delivery constraints are unforgiving: rebuffering kills engagement within seconds, while even slight latency variance breaks live interactivity. Over the last decade building and tuning video CDNs for broadcasters and OTT platforms, I've learned that HLS and DASH are not just player protocols — they dictate your entire edge architecture, from cache key design to origin shielding to TLS session handling. This article breaks down how those protocols actually work on the wire and how to architect your CDN to deliver them reliably under real-world load.

How HLS and DASH Segment Video for Edge Distribution

HLS (HTTP Live Streaming) and DASH (Dynamic Adaptive Streaming over HTTP) both solve the same problem: they turn a continuous video stream into a sequence of small, cacheable HTTP objects that can be distributed through standard CDN infrastructure. Instead of a persistent RTMP or SRT connection to the viewer, the encoder produces segmented media and a manifest that describes it. The CDN then does what it does best — cache and serve HTTP objects at scale.

The critical difference is packaging and now, with CMAF, convergence. Historically, HLS used MPEG-TS segments (.ts) and DASH used fragmented MP4 (fMP4). With Common Media Application Format (CMAF), both protocols can share the same fMP4 segments and only the manifest differs. In my experience, moving to CMAF with fMP4 for both HLS and DASH cut storage and origin egress by nearly 40% for one sports streaming client because we stopped storing two complete segment sets for every rendition.

HLS Manifest Structure and Segment Addressing

HLS uses a master playlist (multivariant playlist) that lists available renditions, each pointing to a media playlist. Media playlists contain an ordered list of segment URIs with duration and sequence numbers. For VOD, playlists are static and cacheable for hours. For live, the media playlist is a sliding window that updates every segment duration.

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720,CODECS="avc1.4d401f,mp4a.40.2",FRAME-RATE=30.000
720p30/chunklist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=960x540,CODECS="avc1.4d401f,mp4a.40.2",FRAME-RATE=30.000
540p30/chunklist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360,CODECS="avc1.4d401f,mp4a.40.2",FRAME-RATE=30.000
360p30/chunklist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=400000,RESOLUTION=426x240,CODECS="avc1.4d401f,mp4a.40.2",FRAME-RATE=30.000
240p30/chunklist.m3u8

# Media playlist: 720p30/chunklist.m3u8
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:18420
#EXT-X-MAP:URI="init.mp4"
#EXTINF:4.000,
seg-18420.m4s
#EXTINF:4.000,
seg-18421.m4s
#EXTINF:4.000,
seg-18422.m4s

I've found that a 4-second segment duration remains the most balanced default for HLS. It keeps manifest size manageable, allows reasonable CDN cache hit ratio, and limits the number of origin requests. Shorter 2-second segments improve ABR responsiveness but double your request rate and can reduce cache efficiency at the edge if not tuned correctly.

DASH MPD Structure and CMAF Alignment

DASH uses a single XML manifest — the Media Presentation Description (MPD) — that describes periods, adaptation sets, and representations. While more flexible than HLS (it supports multiple audio languages, camera angles, and DRM signaling more elegantly), it requires careful templating to remain CDN-friendly.

<MPD type="dynamic" availabilityStartTime="2024-01-15T18:00:00Z" minBufferTime="PT2S" mediaPresentationDuration="PT0H0M0.00S">
  <Period start="PT0S">
    <AdaptationSet mimeType="video/mp4" segmentAlignment="true">
      <SegmentTemplate timescale="90000" initialization="init-$RepresentationID$.mp4"
                       media="seg-$RepresentationID$-$Number$.m4s" startNumber="1" duration="360000"/>
      <Representation id="720p" bandwidth="2800000" width="1280" height="720" codecs="avc1.4d401f"/>
      <Representation id="480p" bandwidth="1400000" width="854" height="480" codecs="avc1.4d401f"/>
    </AdaptationSet>
  </Period>
</MPD>

When using $Number$ templating with CMAF, the segment URLs are deterministic and highly cacheable. Avoid $Time$ templating unless you need variable segment durations — it makes cache key prediction and prefetch logic harder. For both protocols, keep initialization segments (init.mp4 / EXT-X-MAP) immutable and cache them for a year. They never change for a given rendition.

Attribute HLS DASH
Manifest Format m3u8 text playlist (master + media) XML MPD (single file, templated)
Segment Container MPEG-TS or fMP4 (CMAF) fMP4 (CMAF) or WebM
Codec Flexibility H.264, HEVC, requires specific signaling Any ISO BMFF codec, broader signaling
DRM Support SAMPLE-AES, FairPlay MPEG-CENC, Widevine, PlayReady, FairPlay
Low-Latency Mode LL-HLS (partial segments + blocking reload) LL-DASH (chunked CMAF + MPD delta updates)
Default Segment Duration 4-6s (2s for low latency) 2-4s (1s for low latency)
CDN Cache Efficiency Very high — simple URL mapping High — requires templating normalization

For specifications, the IETF Datatracker HLS specification remains the authoritative reference for playlist semantics, while DASH is defined by ISO/IEC 23009-1.

Designing Origin and Edge Caching for Segmented Video Delivery

Video caching is deceptively simple until a live event with 500k concurrent viewers hits your origin. Every viewer needs the latest 3-6 segments at the same time, and if your edge cache is misconfigured, you will see a thundering herd that collapses the origin.

TTL Strategy: Manifests vs. Segments vs. Init

Treat these three object types as entirely different cache classes. I've seen teams apply a single 60-second TTL to everything and wonder why live playback stalls.

For live HLS/DASH, media playlists and MPDs must have short TTLs — typically 1-2 seconds for LL modes, or targetDuration/2 for standard live. For VOD, playlists and MPDs can be cached for hours. Segments and initialization files are immutable once written and should be cached for days or longer. Use Cache-Control: public, max-age=31536000, immutable for segments.

Origin should send Cache-Control: max-age=2, stale-while-revalidate=5, stale-if-error=30 for live manifests. This lets the edge serve slightly stale manifests during revalidation rather than blocking, which smooths out latency spikes when the packager is under load. The HTTP Working Group Specifications detail how stale-while-revalidate prevents request coalescing failures at scale.

Origin Shield, Request Collapsing and Tiered Caching

Never expose your packager or origin directly to global PoPs. Always place an origin shield — a single mid-tier cache region — in front of it. The shield collapses thousands of edge misses into a single origin fetch.

Enable request collapsing (also called request coalescing) at both edge and shield. If 200 edges request seg-18422.m4s in the same 50ms window, only one request should go to the shield, and only one from shield to origin. Without this, segment availability windows create synchronized miss storms every 4 seconds.

# Nginx / CDN edge cache key and TTL example for video
proxy_cache_path /var/cache/video levels=1:2 keys_zone=video:256m inactive=24h max_size=500g;

# Cache key must include rendition path but strip query strings used for tokens
proxy_cache_key "$scheme$host$uri";

# Map TTLs by content type
map $request_uri $video_cache_ttl {
    ~*\.m3u8$          2s;
    ~*\.mpd$           2s;
    ~*init\.mp4$       1y;
    ~*\.m4s$           1y;
    ~*\.ts$            1y;
    default            10s;
}

location ~* \.(m3u8|mpd)$ {
    proxy_cache video;
    proxy_cache_valid 200 2s;
    proxy_cache_use_stale error timeout updating http_500 http_502;
    proxy_cache_lock on;
    proxy_cache_lock_timeout 2s;
    add_header X-Cache-Status $upstream_cache_status;
    proxy_pass http://origin_shield;
}

location ~* \.(m4s|ts|mp4)$ {
    proxy_cache video;
    proxy_cache_valid 200 1y;
    proxy_cache_valid 404 5s; # negative cache to protect origin on bad segment numbers
    proxy_cache_lock on;
    slice 1m; # enable range support for players that use byte-range requests
    proxy_pass http://origin_shield;
}

One detail that matters: cache keys for segments must not include authentication tokens. Validate tokens at the edge via an edge function or signed cookie, then normalize the cache key to the bare URI. I've debugged performance incidents where per-user token query strings created a 0% hit ratio for segments.

If you are already optimizing image delivery with content negotiation, the same principle applies here — keep the cache key clean and push variance to the edge logic. The logic we use for Image CDN Format Negotiation: Serving AVIF, WebP and JPEG XL by Browser is similar: normalize the key, handle format selection at the edge, and never let client-specific headers fragment your cache.

Adaptive Bitrate Ladder Engineering and Client Switching Logic

ABR is where encoding decisions meet edge delivery reality. The ladder you encode determines how smoothly clients adapt, how much bandwidth you waste, and how your CDN cache behaves. A poorly designed ladder causes constant oscillation between renditions and wastes origin storage.

A practical ladder for 1080p delivery that I have used for entertainment VOD and live sports:

  • 1080p @ 4500 kbps — 1920x1080, high motion
  • 720p @ 2800 kbps — 1280x720
  • 540p @ 1400 kbps — 960x540
  • 360p @ 800 kbps — 640x360
  • 240p @ 400 kbps — 426x240
  • 144p @ 200 kbps — 256x144 (audio priority for poor networks)

Keep adjacent renditions within a 1.5x to 2x bandwidth step. Larger gaps force the player to make aggressive jumps that cause visible quality shifts. Include audio-only or low-resolution renditions — players will drop to them rather than rebuffer, and users prefer continued audio over a spinner.

How Players Decide and How the CDN Helps or Hurts

Players use throughput-based and buffer-based heuristics. They measure segment download speed, estimate available bandwidth, and monitor buffer health. The CDN influences both. If your edge introduces high TTFB variance or throttles throughput inconsistently, player estimates become noisy and switching logic oscillates.

Three edge behaviors I've found to stabilize ABR:

First, keep TLS and connection reuse high. Players downloading a segment every 4 seconds should reuse the same HTTP/2 or HTTP/3 connection. Connection coalescing failures add 100-300ms of handshake overhead to every segment and distort bandwidth estimates.

Second, deliver segments at consistent throughput. Avoid burst-and-pause shaping at the edge unless you explicitly pace for ABR. Some CDNs offer bandwidth shaping that sends segments at ~1.2x encoded bitrate to help players estimate correctly, rather than dumping the full file as fast as possible.

Third, support byte-range requests and HTTP/206 properly if you use CMAF chunks. Some players request partial segments for rate estimation. Ensure your origin returns correct Content-Range and Accept-Ranges: bytes.

Media delivery tuning shares lessons with image optimization. Just as Image Compression Quality: SSIM, PSNR and Perceptual Metrics for CDN Optimization uses perceptual metrics to choose quality thresholds, video ladders should be built using VMAF or SSIM, not just bitrate. Encode with per-title ladder optimization where possible — a talking-head stream does not need the same top bitrate as a Premier League match.

Low-Latency Extensions: LL-HLS and DASH Low Latency at the Edge

Standard HLS/DASH latency is 12-30 seconds behind live — three segment durations plus player buffer. For interactive use cases (sports betting, auctions, watch parties), that is unacceptable. LL-HLS and Low-Latency DASH reduce glass-to-glass latency to 2-5 seconds using chunked transfer and blocking playlist reloads.

Both use CMAF chunks: each 4-second segment is subdivided into 200-500ms parts that can be delivered as they are encoded, using HTTP chunked encoding. The player subscribes to the next part before it is complete.

LL-HLS Edge Requirements

LL-HLS introduces partial segments, blocking playlist reload, and delta updates. The player requests _HLS_msn and _HLS_part parameters and the edge holds the request open until the requested part is available. This is fundamentally different from standard CDN caching — you need to support blocking requests and HTTP/2 push hints are deprecated, so preload hints are now used instead.

# LL-HLS media playlist with parts and preload hints
#EXTM3U
#EXT-X-VERSION:9
#EXT-X-TARGETDURATION:4
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=0.85,CAN-SKIP-UNTIL=12.0
#EXT-X-PART-INF:PART-TARGET=0.200
#EXT-X-MAP:URI="init-720p.mp4"
#EXT-X-PROGRAM-DATE-TIME:2024-01-15T18:00:04.000Z
#EXT-X-PART:DURATION=0.200,URI="part-18422.0.m4s",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.200,URI="part-18422.1.m4s"
#EXT-X-PART:DURATION=0.200,URI="part-18422.2.m4s"
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="part-18422.3.m4s"
#EXTINF:4.000,
seg-18422.m4s
#EXT-X-PRELOAD-HINT:TYPE=MAP,URI="init-720p.mp4"

# Blocking request example: client holds until part available
# GET /720p/chunklist.m3u8?_HLS_msn=18423&_HLS_part=2
# Edge holds 200-400ms until encoder publishes part

Your CDN must forward the query string for LL-HLS playlists to the origin shield without caching the blocking response incorrectly. Cache the underlying parts and segments immutably, but do not cache the blocking playlist response for longer than HOLD-BACK. In my deployments, I set Cache-Control: no-cache for blocked playlist responses at the edge and cache only completed parts.

For DASH low latency, the equivalent is chunked CMAF with availabilityTimeOffset and MPD patch/delta updates. The edge needs to support chunked transfer encoding end-to-end and must not buffer the entire response before forwarding. Disable any edge buffering or gzip for chunked segments.

Web performance guidance from web.dev Articles on streaming and buffering is useful context here for player-side buffer tuning, but edge tuning is what determines whether low-latency actually works under load.

Securing and Monetizing Streams: Token Authentication, DRM and Watermarking

Video is high-value content and open URLs will be scraped and restreamed within minutes. Every production system I operate uses at least two layers: edge token authentication for access control and DRM for content protection.

Signed URLs and Signed Cookies at the Edge

Signed URLs carry an expiry, client IP binding optionally, and an HMAC signature over the path. The edge validates without contacting origin. Signed cookies are preferable for HLS/DASH because the player makes dozens of segment requests — you do not want to sign every segment URL individually.

Flow: viewer authenticates via your API, receives a Set-Cookie: __Secure-CDN-Token with policy covering /live/event123/*, then all manifest and segment requests carry that cookie. Edge function validates HMAC and expiry, strips cookie from cache key, and serves from cache. Rotate keys every 24 hours and support multiple active keys during rotation.

Geofencing and concurrency limiting should also happen at the edge. Reject requests outside allowed regions before they hit cache, and use a lightweight edge KV store to count concurrent streams per token. Doing this at origin adds latency and cost.

DRM and Forensic Watermarking

For premium content, use MPEG-CENC with multi-DRM: Widevine for Chrome/Android, FairPlay for Safari, PlayReady for Edge. The CDN should not terminate DRM license delivery — route license requests to a dedicated DRM provider. But the CDN must correctly forward DRM signaling in manifests: EXT-X-KEY for HLS SAMPLE-AES and ContentProtection descriptors for DASH.

For high-risk live events, consider client-side or edge-inserted forensic watermarking (A/B variant manifests). The edge can serve variant playlists per session that embed a user ID in the segment URLs, allowing you to trace leaks. This requires per-session playlist generation at the edge, which is CPU-intensive — benchmark your edge compute before enabling it for large audiences.

Tuning Edge Performance: Prefetch, Connection Coalescing and Bandwidth Shaping

The last mile of tuning separates a video CDN that works from one that feels instant. After the manifest and segment cache is correct, focus on connection and delivery shaping.

Prefetch and Predictive Segment Fetch

For live, the next segment is predictable. Once seg-18422.m4s is requested, seg-18423.m4s will be requested in ~4 seconds. Some CDNs offer origin prefetch where the shield fetches N+1 as soon as N is requested. I've used this to cut live segment TTFB from 80ms to 15ms at the edge because the segment is already warm before the player asks.

Be careful with aggressive prefetch for VOD — players may skip ahead or abandon playback, and you will waste origin egress. Limit prefetch to live streams and the next one or two segments only.

HTTP/2, HTTP/3 and Connection Reuse

Video players are sensitive to connection establishment overhead. Prioritize HTTP/3 (QUIC) where supported, as it handles packet loss better on mobile networks and eliminates head-of-line blocking. Ensure your edge presents a consistent certificate covering all video subdomains so browsers can coalesce connections — e.g., *.video.example.com rather than separate certs for hls.video.example.com and dash.video.example.com.

Enable BBR congestion control at the edge if available. BBR handles varying last-mile bandwidth more smoothly than CUBIC for video, reducing rebuffering on fluctuating mobile links. I measured a 12-18% reduction in rebuffer ratio after switching edge congestion control to BBR for a mobile-heavy audience in Southeast Asia.

Bandwidth-based optimization is not unique to video. The same tuning mindset applies to images — see Responsive Images: srcset, sizes and Art Direction for Every Screen for how we reduce wasted bytes by matching delivery to viewport and network conditions. Video just amplifies the cost of getting it wrong by orders of magnitude.

Finally, monitor what actually matters: rebuffer ratio, average bitrate delivered, startup time, and edge cache hit ratio by object type. Segment hit ratio should be 98%+ for popular live events; manifest hit ratio will be lower by design due to short TTLs. If your segment hit ratio drops below 95%, check cache key fragmentation or shield bypass before scaling origin.

Frequently Asked Questions

Should I use HLS or DASH if I can only support one protocol?

Choose HLS if you need broad device compatibility, especially iOS Safari which still requires HLS. Choose DASH if you need advanced features like multi-period ads, multiple audio languages with fine-grained signaling, or MPEG-CENC multi-DRM without extra mapping. In practice, use CMAF fMP4 segments and generate both manifests from the same segments — the incremental cost is minimal and you cover all players. Most modern packagers do this by default.

What segment duration should I use for live versus VOD?

For VOD, use 4-6 second segments to maximize cache efficiency and minimize manifest size. For standard live, 4 seconds with 3 segments in the window balances latency and stability. For low-latency live, use 4-second CMAF segments divided into 200-500ms parts with LL-HLS or LL-DASH — do not reduce the segment duration to 1 second as an alternative, as it increases request rate and origin load without the benefits of chunked delivery.

How do I prevent origin overload during traffic spikes for live events?

Implement three layers: origin shield with request collapsing, short stale-while-revalidate on manifests, and long immutable caching on segments. Validate tokens at the edge and strip them from cache keys. Pre-warm the shield before the event starts by requesting the init segments and first media segments. If you expect >100k concurrent viewers, run load tests that simulate synchronized segment requests every target duration — that pattern is what breaks untested setups.

Do I need DRM if I already use signed URLs?

Signed URLs control access to the stream but do not prevent capture or redistribution once a viewer is authorized. For free or ad-supported content, signed URLs plus geofencing may be sufficient. For subscription or pay-per-view content, use DRM (FairPlay, Widevine, PlayReady) in addition to edge authentication. The two layers are complementary: tokens stop unauthorized access at the CDN, DRM prevents decryption and sharing outside the player.

Related Articles

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