Origin Range Requests: Serving large files without range-request tuning
Cloudflare reworked range-request caching so a video seek no longer pulls a 128 MiB chunk from origin; it now fetches and caches just the needed ranges for large files.
Extract
A video player seeking to the middle of a movie asks for a few megabytes. Under our old range caching, that one request could make Cloudflare pull 128 MiB from the origin and move it between data centers, sometimes after the viewer had already left. Range requests are how large files get delivered, and caching them naively easily leads to wasted network bandwidth and wasted disk operations when running a CDN at scale. So we rebuilt how our cache handles them. For R2, Cloudflare's object storage, p99 time to first byte dropped by about half, and a follow-up improvement cut tiered cache traffic for R2 and similar workloads by about 6%. Today, we're making that new implementation available as Origin Range Requests , a self-service feature on all plans. There's no range size to tune and no need to contact support. Turn it on with a Cache Rule , and Cloudflare handles the rest. A request for part of a file An HTTP range request asks for part of a resource instead of the whole resource. The client sends a normal GET with a Range header: GET /movie.mp4 HTTP/1.1 Host: example.com Range: bytes=1048576-2097151 If the server supports the request, it returns 206 Partial Content and identifies the returned bytes with Content-Range : HTTP/1.1 206 Partial Content Content-Range: bytes 1048576-2097151/524288000 Content-Length: 1048576 Range requests are common for large objects. A video player can request bytes around a seek position instead of the whole file. A download manager can resume after an interruption. A package manager can fetch part of an artifact. A client can inspect the end of a file without downloading everything before it. The client decides whether to request a range, and Cloudflare does not change that request. Instead, Origin Range Requests changes how Cloudflare satisfies it. The range-size problem Cloudflare has supported range-request caching for years via static zone configuration. The previous implementation resembled the cache-slicing described in an nginx blog post : each client request was mapped to fixed-size ranges of the file, with the range size and file extensions configured per zone. The size of each range created a tradeoff: small ranges led to many small origin requests and memory pressure on Cloudflare from coalescing all of the requests in-memory, while large ranges caused Cloudflare to download much more data than the client requested. Updates to the range size itself would invalidate cache entries, since the size was …
Explained
What happened
Cloudflare rebuilt how its CDN caches HTTP range requests and shipped it as Origin Range Requests, a self-service setting on all plans, turned on with a Cache Rule (origin_range_requests: { "mode": "on" }).
A range request asks for part of a file:
GET /movie.mp4 HTTP/1.1
Range: bytes=1048576-2097151
HTTP/1.1 206 Partial Content
Content-Range: bytes 1048576-2097151/524288000
Results on R2 (Cloudflare object storage), the biggest internal user:
- p99 time to first byte cut roughly in half, on both hits and misses.
- ~6% less tiered-cache traffic, which is petabytes less data moved between data centers per day.
Why it works
The old design (like nginx cache slicing) mapped each request to fixed-size slices, with the slice size configured per zone:
- Big slices waste bytes. R2 used 64 MiB slices, so a small read crossing a boundary could pull 128 MiB from origin and push it across several data centers, sometimes after the viewer had left.
- Small slices waste memory. One request becomes a long line of tiny origin fetches, each holding memory.
- Changing the size invalidated the cache (the size was part of the cache key), and concurrent requests in the same slice blocked each other.
The new design (on Pingora, their Rust proxy) reuses something the cache already had: every cached file is stored on disk in fixed-size chunks. So the "range size" is simply the chunk size:
- Look up which requested bytes are already cached (or currently streaming in).
- Serve those directly.
- Fetch only the missing bytes from origin, aligned to chunk boundaries, and store them as normal chunks.
- Stitch the pieces together and return exactly the requested bytes with a correct
Content-Range.
A second request for bytes already being downloaded attaches to the in-flight download instead of starting another origin fetch. Before stitching, it checks the ETag and total size match across all pieces, and honours If-Range, so it never mixes bytes from two versions of a file.
The bigger pattern
This is request coalescing plus alignment: turn arbitrary client requests into a small, bounded set of aligned backend fetches, and share in-flight work. The waste becomes bounded and predictable instead of depending on a tuning knob. The same idea appears in page caches, block storage and database buffer pools.
What it means in practice
- For video delivery, seeking is range requests. The player asks for a few MB around a timestamp, and the CDN's alignment strategy decides origin egress cost.
- If you serve large files (video, model weights, exports) behind a CDN, make sure the origin supports
Rangeand returns stableETags, otherwise partial caching can't work safely. - In your own services, look for "slice size" style knobs exposed to users. They often hide an internal limitation that better internal chunking could remove.