cloudflare-range-cache
v1.0.0
Published
Cache HTTP Range (206 Partial Content) responses at the Cloudflare edge without Enterprise — a dependency-free Worker that chunk-caches large media and reassembles correct 206 responses.
Maintainers
Readme
cloudflare-range-cache
Cache HTTP Range (206 Partial Content) responses at the Cloudflare edge — without an Enterprise plan.
A tiny, dependency-free Cloudflare Worker that makes large media (mp4/webm/…) served over Range
requests actually cache on Free/Pro/Business plans, cutting origin egress dramatically.
In production at kinobd.net this reclaimed ~19% of a zone's total traffic (≈240 GB/day, ~33k previously-uncached requests/day) with a single Worker and no origin changes.
The problem
Video and audio players fetch files with HTTP Range requests (Range: bytes=0-). Cloudflare
does not cache 206 Partial Content responses on any plan below Enterprise — every range
request is a cache BYPASS that goes straight to your origin. Caching whole files doesn't help
either: a single movie can be many GB, well past Cloudflare's ~512 MB per-object cache limit.
The solution
This Worker splits each request into fixed-size, boundary-aligned 4 MB chunks, stores every
chunk in the edge cache as its own cacheable 200 object (…?__c=<n>), and reassembles a
correct 206 for the client. The player is none the wiser — it asks for a byte range and gets
exactly that range.
Two viewers of the same file never request identical byte ranges, but aligned chunks always coincide — which is the only way a shared cache actually accumulates.
It is fail-open: any error, timeout, or unexpected origin response falls through to a plain passthrough. Delivering the media always wins; the Worker can never become a point of failure.
Quick start
npm i -g wrangler # or: npx wrangler
git clone https://github.com/KinoBD/cloudflare-range-cache
cd cloudflare-range-cache
# edit wrangler.toml → set your route (the media hostname/path you want cached)
wrangler deployPoint a route at the media path you serve (e.g. cdn.example.com/media/*). That's it — no origin
changes. You can confirm it's active from the response header X-Chunk-Cache: worker and rising
cf-cache-status: HIT on the internal chunk requests.
How it works
- Request has a
Range? If not, passthrough. - Compute the aligned chunk(s) that cover the requested bytes.
- For each chunk: serve from
caches.default; on a miss, fetch that exact aligned range from origin, repackage the206as a cacheable200(X-Full-Length,X-Src-Type), store it. - Stream the requested slice of each chunk back to the client as a single correct
206— headers first, body as chunks arrive, so playback starts immediately.
Configuration
Edit the constants at the top of src/worker.js:
| Constant | Default | Meaning |
|---|---|---|
| CHUNK | 4 * 1024 * 1024 | Chunk size. Smaller = more subrequests; larger = worse cross-viewer reuse. |
| MAX_CHUNKS | 64 (256 MB) | Guard against absurd ranges; larger ranges are streamed, so memory is constant. |
| TTL | 604800 (7 days) | Edge cache lifetime. Media segments are immutable. |
Limitations
- Origin must support
Range(respond206withContent-Range). If it doesn't, the Worker passes through. - Suffix ranges (
bytes=-500) are passed through (rare in practice). - Content type is taken from the origin response, so mixed content (e.g.
.vttalongside mp4) under the same route works.
Why this exists
Built and battle-tested by kinobd.net, an online cinema aggregator, to serve large video over Cloudflare at scale without Enterprise pricing. Open-sourced because the technique is broadly useful and there was no clean, self-contained implementation.
