Why Chrome Sends Hundreds of 206 Requests for One MP4 and How FFmpeg Fixes It
An MP4 video with a misplaced moov box and poor interleaving caused Chrome to issue an initial three 206 requests followed by thousands of range‑jumping requests, inflating CDN traffic, while re‑encoding the file with FFmpeg faststart eliminated the excess requests and restored normal bandwidth usage.
Background
A 1 GB training video that should have generated only a few terabytes of traffic for 2,500 viewers was observed to consume more than 120 TB of CDN bandwidth.
Phenomenon Identification
Traffic pattern
CDN logs showed that the top‑consuming files were a handful of MP4 videos, each about 1 GB and 30 minutes long. When the video was played in Chrome with the developer tools open, the full playback downloaded 8 GB and generated 3,700 HTTP 206 range requests.
Three 206 requests appear at the start of playback.
After the third request the browser repeatedly issues additional 206 requests.
The Range header values jump back and forth between distant file offsets.
Verification with a smaller file
A second video (500 MB, 40 minutes) exhibited the same pattern of multiple 206 requests, confirming that the behaviour is not size‑specific.
Investigation
MP4 file structure
ftyp – file type (header)
moov – metadata (index information)
mdat – actual audio/video dataThe moov box contains the index that tells the player where each sample resides. The player must read moov before it can locate the mdat payload. moov holds all index tables (e.g., stco, stsz, stsc). mdat stores the raw media streams.
If moov is placed at the end of the file, the player must seek to the file tail, read moov, then seek back to mdat, causing extra HTTP 206 requests.
Browser playback flow
Chrome uses the FFmpegDemuxer to parse MP4 files. The simplified sequence is:
Read ftyp + moov → Seek to mdat → Start playbackWhen moov is at the end, the demuxer performs two additional seeks, resulting in three initial 206 requests.
Source‑code analysis
The function mov_find_next_sample() selects the next sample based on DTS (decoding timestamp) difference and file offset. For poorly interleaved files the “closest” DTS may be far away in the file, causing the demuxer to jump back and forth.
static AVIndexEntry *mov_find_next_sample(AVFormatContext *s, AVStream **st) {
for (i = 0; i < s->nb_streams; i++) {
// if DTS diff ≤ 1 s, pick the earlier file position
// else pick the smallest DTS
}
return sample;
}When audio and video packets are not interleaved, the algorithm repeatedly selects distant offsets, generating many range requests.
Test verification
A deliberately poorly interleaved video ( longbad.mp4) was processed with FFmpeg tracing. The default interleaved read produced 5,078 HTTP 206 requests for a 5‑second clip.
ffmpeg -i https://example/longbad.mp4 -ss 60 -t 5 -y output.mp4 -loglevel trace 2>&1 | grep "Range"
# → 5078 requestsDisabling interleaved read ( -interleaved_read 0) reduced the count to three requests.
ffmpeg -interleaved_read 0 -i https://example/longbad.mp4 -ss 60 -t 5 -y output.mp4 -loglevel trace 2>&1 | grep "Range"
# → 3 requestsRoot Cause Analysis
Why three initial 206 requests?
The moov box resides at the file end. The player performs:
1. Seek to file end, read ftyp+moov → 206 #1
2. Read moov (still at tail) → 206 #2
3. Seek to mdat and start playback → 206 #3Why many subsequent 206 requests with jumping ranges?
The audio and video packets are stored in large, non‑interleaved blocks (e.g., all audio first, then video). FFmpeg’s mov_find_next_sample() prefers the earliest file offset when DTS differences are ≤ 1 s. Because the “earliest” packet may be far from the current position, the demuxer repeatedly seeks between distant audio and video blocks, each seek generating a new HTTP 206 request.
Why does re‑encoding with FFmpeg solve the problem?
Running:
ffmpeg -i bad.mp4 -c copy -movflags faststart good.mp4moves the moov box to the beginning of the file and re‑interleaves the streams. The resulting file is read sequentially, producing a single 206 request and bandwidth consumption equal to the file size.
Solution
For short‑form videos, process the source MP4 with FFmpeg using the -movflags faststart option (or -interleaved_read 1) to ensure:
The moov box is placed at the file header.
Audio and video packets are interleaved in time order.
This eliminates excessive range requests and reduces CDN bandwidth usage.
For long‑form content, adopt adaptive streaming formats such as HLS or DASH, which split the media into small segments and allow progressive download, providing better bandwidth efficiency and smoother playback.
References
The investigation of excessive FFmpeg requests: https://blog.dreamfever.me/posts/2024-06-09-poor-performance-of-ffmpeg-i-url/#reference
Endless canceled HTTP requests when playing an MP4 in a <video> tag: https://issues.chromium.org/issues/40292515
Chromium source (mov.c): https://source.chromium.org/chromium/chromium/src/+/main:third_party/ffmpeg/libavformat/mov.c;l=11048
FFmpeg source (mov.c lines 9767‑9795): https://github.com/FFmpeg/FFmpeg/blob/94f2274a8b61438572f0873ccf430e55ce0e0e2b/libavformat/mov.c#L9767-L9795
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
