How HTTP Works
4xx · Client error
< HTTP/1.1 416 Range Not Satisfiable

416 Range Not Satisfiable: Causes and Fixes

416 Range Not Satisfiable means the requested byte range lies outside the resource. Find why resumed downloads and video requests fail, and how to fix it.

Reviewed 3 min readintermediate4 sourcesTry itMarkdown
Cacheable
Only with explicit freshness
Retry?
After fixing the Range header
Usually sent by
Origin or CDN
Spec
RFC 9110 §15.5.17
On this page

TL;DR: 416 means the Range you sent starts beyond the end of the resource (or is otherwise invalid for its current size). Read Content-Range: bytes */<size> in the response, then finish, or throw away your partial data and re-fetch from byte 0.

What it means

The client asked for part of a resource, and the server cannot give any of it. RFC 9110 §15.5.17 says the server should include a Content-Range with an unsatisfied-range value that reports the current length:

GET /downloads/app-2.4.1.tar.gz HTTP/1.1
Host: example.com
Range: bytes=52428800-

HTTP/1.1 416 Range Not Satisfiable
Content-Range: bytes */31457280
Content-Type: text/html

The client has 50 MiB (52,428,800 bytes) and the file is now 30 MiB (31,457,280 bytes), so there is nothing past offset 52,428,800 to return.

What counts as unsatisfiable: the first byte position of every range is at or beyond the resource length, or a suffix range asks for zero bytes (bytes=-0), or the resource has zero length. A last-byte position that overshoots the end is fine and is clamped.

Resource length 5000

Range: bytes=0-999999   -> 206, bytes 0-4999/5000   (clamped, fine)
Range: bytes=5000-      -> 416, bytes */5000        (first byte past the end)
Range: bytes=-100       -> 206, bytes 4900-4999/5000

Servers may also answer a malformed or abusive Range with 200 and the full body rather than 416. Syntactically broken ranges are supposed to be ignored.

Who sent it?

416 comes from whichever layer evaluated the range against the object size. For static files that is usually the origin web server, but a CDN that has cached the object answers from its own copy. Check Server, Via, Age and X-Cache to find out which. Mismatched sizes between layers are the classic cause: the CDN holds the old 50 MiB file, the origin holds the new 30 MiB one, and a client resuming against one while the other answers gets 416.

Common causes

  1. Resuming a complete download. The client sends Range: bytes=<size>- for a file it already has. Servers answer 416, and tools react differently: wget says the file is already fully retrieved and exits cleanly, some scripts treat any non-2xx as a failure.
  2. The file changed. A new build was uploaded under the same name, so the partial local copy is longer than the new file. This is the case If-Range is designed to catch; see 206.
  3. Inconsistent CDN or load-balanced origins reporting different sizes for the same URL.
  4. Client bug: computing the range with an off-by-one (bytes=0-<size> is fine, but bytes=<size>- is not), or using the compressed size where the server uses the uncompressed size or the reverse.
  5. Zero-byte object. Any Range on an empty file is unsatisfiable. Some video players probing an empty placeholder file surface this as a 416.
  6. Truncated cache. A browser cached a partial response for a video and later asked for a range using stale metadata. A hard reload clears it.

Fix it

For clients, treat 416 as information, not just failure:

async function resume(url, localBytes) {
  const res = await fetch(url, { headers: { Range: `bytes=${localBytes}-` } })

  if (res.status === 416) {
    const m = /bytes \*\/(\d+)/.exec(res.headers.get('Content-Range') ?? '')
    if (m && Number(m[1]) === localBytes) return 'already complete'
    return restartFromZero(url) // file changed; local partial is useless
  }
  if (res.status === 200) return restartFromZero(url) // server ignored Range
  // 206: append res.body to the partial file
}

For servers, return Content-Range: bytes */<size> on every 416, and on nginx cap pathological multi-range requests:

location /downloads/ {
    max_ranges 1;   # 0 disables range support entirely; default is unlimited
}

If behind a CDN, purge the object after replacing a file in place, or better, publish new versions under new URLs (content-hashed filenames) so ranges can never straddle two versions.

Reproduce it

# Ask for a range past the end
curl -i -r 999999999- https://example.com/files/small.txt
# HTTP/2 416
# content-range: bytes */1024

# wget resume on a finished file
wget -c https://example.com/files/small.txt
# HTTP request sent, awaiting response... 416 Requested Range Not Satisfiable
# The file is already fully retrieved; nothing to do.

Frequently asked questions

What does 416 Range Not Satisfiable mean?

The request had a Range header, but none of the ranges overlap the current size of the resource, for example asking for bytes=5000- from a 3000-byte file. The response normally carries Content-Range: bytes */3000 so the client can see the real size.

Why do I get 416 when resuming a download?

Usually because the file on the server is smaller than your partial copy (it was replaced or truncated), or because the download was already complete and the client asked for bytes starting at the end. wget prints that the file is already fully retrieved in the second case.

How do I fix 416 in the browser?

Hard-reload or clear the cached entry for the URL. A stale cached partial of a video or PDF that has since been replaced is a common cause. If it persists, the server or CDN is returning wrong sizes.

What should a client do after receiving 416?

Read Content-Range: bytes */N to learn the current size, then either conclude the download is finished (if your local size equals N) or discard the partial data and restart with a plain GET.

Is a Range header with an end beyond the file size an error?

No. bytes=0-999999 on a 5000-byte file is satisfiable and the server returns 206 with the bytes it has, labelled bytes 0-4999/5000. Only a first-byte position at or past the end of the resource makes the range unsatisfiable.

Sources

  1. MDN Web Docs: 416 Range Not Satisfiabledeveloper.mozilla.org
  2. RFC 9110 Section 15.5.17: 416 Range Not Satisfiablerfc-editor.org
  3. RFC 9110 Section 14.4: Content-Rangerfc-editor.org
  4. nginx: max_rangesnginx.org

Keep going

Browse /search