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
Rangerequest 416s while the same URL without aRangeheader returns 200. - Small files 416 on any
Rangerequest 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
-
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.
-
A range on a tiny or zero-length resource - Edge case, but a persistent one. nginx historically returned 416 for a
Rangerequest against files below a couple of bytes (trac ticket #1031), and any server will 416 abytes=0-request against an empty body — there’s no byte 0 to serve. Health-check probes and prefetchers that blindly attach aRangeheader to every request trip this on placeholder files, favicons, and generated responses that happen to be empty. -
A malformed or reversed range -
bytes=2000-1000(start after end), a negative-suffix range larger than the file (bytes=-99999on a 500-byte file resolves to the whole thing, which is fine, but implementations disagree), or a proxy that rewrites theRangeheader 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: bytestells you whether the server supports partial content at all — if it’s absent or saysnone, sending aRangeheader is pointless and you should stop.Content-Lengthtells 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-Lengthand 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-Rangewith the ETag orLast-Modifiedyou captured earlier. If the resource changed, the server returns a full 200 instead of a broken partial — that’s exactly the guardIf-Rangeexists for. - Don’t attach a
Rangeheader to resources that report noAccept-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) or200 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-Lengththat 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