← Back to Wiki
Networking / Self-Hosting

Large Downloads Fail Only When You're Away From Home: Cloudflare's 100 MB Proxied Response Cap

Downloading a movie from a self-hosted Jellyfin server worked perfectly at home and failed on every other network. Streaming was fine in both places. Nothing in Jellyfin's logs looked wrong. The cause was a documented Cloudflare limit that most self-hosters never hit, because most self-hosted traffic is small — and the shape of the failure makes it look like an application bug for as long as you're willing to believe it.

Share on X

The limit

Cloudflare's Free plan caps the body of a single proxied response at 100 MB. That's per response, not per month, and it applies to anything going through an orange-clouded DNS record. Ask a proxied origin for a 40 GB file and Cloudflare terminates the connection almost immediately — it doesn't stream 100 MB and stop politely.

This bites self-hosters with:

Why it looks like an app bug

Two things disguise it, and both are worth internalising because they generalise to any CDN-in-front problem:

It works at home. Mobile apps like Jellyfin's typically find the server by LAN discovery or a direct IP when you're on the same network, so they never touch the public hostname and never traverse Cloudflare. The proxied path is only exercised when you're away — which makes it look like a client, ISP or "mobile data" problem rather than infrastructure.

Streaming keeps working. Transcoded and direct-play streaming is delivered as many small segments, each far below the cap. Only the single-response full-file download crosses it. "Playback is fine, downloads are broken" is a genuinely confusing signal, and it points away from the network.

App at home LAN discovery direct — never touches the CDN ✓ App elsewhere public hostname Cloudflare proxy 100 MB per response segments ✓ full file ✗ Reverse proxy + media server
Why it only fails away from home. The same app, the same server — but only the off-LAN path crosses the proxy, and only the full-file response crosses the cap.

Confirming it, rather than guessing

Read the reverse proxy's access log at the origin — Caddy here, but the same fields exist in nginx or Traefik. You are looking for three things together:

  1. A request to the download endpoint (/Items/<id>/Download for Jellyfin).
  2. Cloudflare headers on the requestCf-Ray and Cdn-Loop: cloudflare. That proves the request came through the proxy and not directly, which is the fact the whole diagnosis turns on.
  3. A duration in tens of milliseconds with an error like writing: http2: stream closed. A connection that dies that fast on a multi-gigabyte file never got going; that's not a slow network or a timeout.
journalctl -u caddy -f | grep -i download
# or, if you log to a file:
tail -f /var/log/caddy/access.log | grep Download

The detail that rules out the obvious workaround: clients retry with a Range header to resume, and those retries die just as fast. Cloudflare rejects based on the advertised remaining length, not on bytes accumulated so far — so "just resume it" cannot get you past a file that's over the cap, and chunked client-side downloading doesn't rescue it either.

The fix: stop proxying that one hostname

The cap isn't a setting you can raise on any economically sane plan, so the fix is to take that single hostname off Cloudflare's proxy while leaving the rest of your domain alone.

In the Cloudflare dashboard, set that record to DNS only (grey cloud) instead of Proxied (orange cloud). What you give up on that hostname is real and worth stating: origin IP hiding, Cloudflare's DDoS absorption, and its WAF. What you get back is unlimited response sizes.

If hiding your home IP was the reason for proxying in the first place, point the DNS-only record at a cheap VPS and forward the traffic — a raw TCP passthrough on 80/443 to your reverse proxy at home keeps the origin hidden without a CDN in the response path. Two things to plan for if you do that:

Prevention

The one-line version: Cloudflare's Free plan won't proxy a response body over 100 MB, streaming survives it because segments are small, and full-file downloads don't — so set that one hostname to DNS-only, and if you need the origin hidden, forward through a small VPS instead of a CDN.