Views: 109

416 Range Not Satisfiable: Why Byte Ranges Fail

416 Range Not Satisfiable means your Range header asked for bytes past the file's end. Find the real length in 3 checks. Free instant check, no sign-up.

Check your domain for this issue now

Free, no sign-up. Runs the exact check this guide describes and shows what to fix.

Problem

A request with a Range header comes back 416 Range Not Satisfiable. The URL is right, the resource exists, and a plain GET returns it fine. But you asked for a specific slice of bytes — Range: bytes=1000-2000 — and the server checked that slice against the resource’s current size and found that none of it exists. You’re asking for bytes that are off the end of the file.

Symptoms

  • A download manager resuming a paused download fails with 416 instead of continuing.
  • A video or audio player throws 416 when the user seeks, or on the very first byte-range probe.
  • A Range request 416s while the same URL without a Range header returns 200.
  • Small files 416 on any Range request even though larger files on the same server work.
  • The 416 response carries Content-Range: bytes */<n> where <n> is smaller than the byte offset you requested.

What 416 Actually Means

RFC 9110 (§15.5.17) defines 416 for the case where “none of the ranges in the request’s Range header field overlap the current extent of the selected resource.” Byte ranges are zero-indexed and inclusive, so bytes=0-511 is the first 512 bytes. Ask for bytes=1000-2000 from a 512-byte file and there is nothing there to send — every requested byte sits past the end.

The important detail is that 416 is not a generic “bad request.” It’s specifically a mismatch between the range you named and how long the resource is right now. The spec also says a 416 response SHOULD include a Content-Range header stating the unsatisfied range and the real length: Content-Range: bytes */12777. That * means “your range was unsatisfiable,” and 12777 is the current size. The server isn’t just rejecting you — it’s handing you the number you got wrong so you can retry correctly.

Honoring Range at all is optional. A server that doesn’t support partial content just ignores the header and returns 200 with the whole body. So a 416 is actually a signal that the server does do ranges (it advertises Accept-Ranges: bytes), which means the problem is your arithmetic, not the server’s capability.

Top 3 Causes

  1. A stale Content-Length after the file changed - The dominant real cause. Your client records the resource’s length on the first request, then resumes or seeks against a byte offset derived from that length. If the file was replaced with a shorter one, or a CDN in front of the origin started serving a gzip’d or transformed variant with a different byte count, the offset your client trusts is now past the end of the resource it’s actually talking to. Every resumed range lands out of bounds and 416s. Nothing is broken except the client’s memory of how big the file was.

  2. A range on a tiny or zero-length resource - Edge case, but a persistent one. nginx historically returned 416 for a Range request against files below a couple of bytes (trac ticket #1031), and any server will 416 a bytes=0- request against an empty body — there’s no byte 0 to serve. Health-check probes and prefetchers that blindly attach a Range header to every request trip this on placeholder files, favicons, and generated responses that happen to be empty.

  3. A malformed or reversed range - bytes=2000-1000 (start after end), a negative-suffix range larger than the file (bytes=-99999 on a 500-byte file resolves to the whole thing, which is fine, but implementations disagree), or a proxy that rewrites the Range header into something the origin can’t satisfy. Multi-range requests where every part is out of bounds also collapse to a single 416.

Diagnose with DechoNet

  • HTTP Check to read the resource’s real headers on a clean GET. Two fields decide everything. Accept-Ranges: bytes tells you whether the server supports partial content at all — if it’s absent or says none, sending a Range header is pointless and you should stop. Content-Length tells you the resource’s current size; if the byte offset your client is requesting is larger than that number, you’ve found your 416. When the length the check reports differs from what your client cached, the file changed under you — that’s the stale-length case, and it’s the answer nine times out of ten.

Resolution Checklist

  • Read the current Content-Length and confirm your range’s start byte is smaller than it. If it isn’t, the resource is shorter than your client thinks.
  • On a 416, parse the response’s Content-Range: bytes */<n> to get the authoritative current length, then re-request a range inside [0, <n>). You don’t need to restart from zero.
  • For resumed downloads, send If-Range with the ETag or Last-Modified you captured earlier. If the resource changed, the server returns a full 200 instead of a broken partial — that’s exactly the guard If-Range exists for.
  • Don’t attach a Range header to resources that report no Accept-Ranges: bytes. The server may ignore it (200) or reject it (416); neither is what you wanted.
  • Behind a CDN, confirm the edge and origin agree on the byte length, and that compression or image transforms aren’t changing the size between what the client measured and what the origin now serves.
  • Re-run the request and confirm you get 206 Partial Content (range honored) or 200 OK (full body), not another 416.

When to Escalate

  • If clients 416 on resumed downloads across many files at once, something changed the byte length of your resources in bulk — a redeploy that regenerated assets, a new compression layer, or a cache serving transformed variants. That’s an infrastructure change, and it belongs with whoever owns the origin or the CDN config.
  • If a media server 416s on legitimate seeks that fall well inside the file, the server may be miscomputing the resource length (dynamic bodies, streamed generation, or a Content-Length that doesn’t match the bytes actually available). A range validated against the wrong length is a server bug, not a client one.

Related Tools

Related Guides

Share this guide

[Ad] Guide Detail Inline
← Back to All Guides