HTTP 416 Range Not Satisfiable

HTTP 416 Range Not Satisfiable means a GET request’s Range header specified a byte range that does not exist in the resource. The range starts beyond the end of the file, or the range syntax is invalid. The server cannot fulfill the partial content request and returns 416 with a Content-Range header showing the actual resource size, giving the client the information needed to correct the range and retry.

HTTP 416 quick reference →

Quick reference

Code416
NameRange Not Satisfiable
Category4xx Client Error
SpecificationRFC 9110 §15.5.17
Required response headerContent-Range: bytes */<total-size>
Cacheable?No

The Content-Range header in 416 responses

RFC 9110 requires a 416 response to include a Content-Range header that indicates the actual size of the resource. The range portion uses a special * syntax meaning the range is unknown or unsatisfiable, while the total remains the actual file size:

GET /files/video.mp4 HTTP/1.1
Range: bytes=1000000000-1000001000

HTTP/1.1 416 Range Not Satisfiable
Content-Range: bytes */524288000
Content-Type: text/plain

The requested range is not satisfiable. Resource is 524288000 bytes.

The value */524288000 tells the client: the range you requested is invalid, and the resource is 524,288,000 bytes (500MB). The client can use this information to correct its range request — for example, adjusting a resumable download that assumed the file was larger than it actually is.

How byte ranges work

The Range header uses the format bytes=start-end where both values are zero-indexed byte offsets. A few forms are valid:

Range headerMeaningValid for 1000-byte file?
bytes=0-499First 500 bytesYes → 206
bytes=500-999Last 500 bytesYes → 206
bytes=-500Last 500 bytes (suffix)Yes → 206
bytes=500-From byte 500 to endYes → 206
bytes=1000-1499Starts at byte 1000No → 416 (file ends at 999)
bytes=200-100Start greater than endNo → 416 (invalid range)

A range is unsatisfiable when the first-byte-pos exceeds the resource length, when start is greater than end, or when a suffix length is zero (though most servers handle the last case by returning the full resource).

Common causes of 416

Resumable download with stale file size: The most common production cause. A download manager or client calculated the remaining range based on a cached file size, but the file on the server changed (was updated, replaced, or truncated). The client requests bytes beyond the end of the new, smaller file.

# Download manager knows file is 100MB, downloaded first 60MB:
Range: bytes=62914560-104857599   # requests 60MB-100MB

# Server replaced the file with a 50MB version:
HTTP/1.1 416 Range Not Satisfiable
Content-Range: bytes */52428800  # file is 50MB
# Client must restart the download from scratch

Concurrent file modification: A server generates dynamic content and the content length changes between the HEAD request (which returned the file size) and the subsequent ranged GET.

Off-by-one error in range calculation: Byte offsets are zero-indexed. A 1000-byte file has bytes 0–999. A range of bytes=1000- (starting at byte 1000) is unsatisfiable because the last valid byte is index 999.

Client bug sending overlapping or incorrect ranges: Multi-part download clients that split a file into chunks for parallel downloading can have bugs where a chunk range is calculated incorrectly.

Server-side implementation

Most web servers (nginx, Apache, Caddy) implement range request handling automatically. You only need to implement it manually in application code for dynamically generated content or custom streaming endpoints.

nginx: Range requests are handled automatically for static files. Ensure the max_ranges directive is not set to 0 (which disables ranges):

server {
    location /downloads/ {
        max_ranges 10;   # allow up to 10 ranges per request (default: unlimited)
        # nginx returns 416 automatically for out-of-bounds ranges
    }
}

Express.js manual range handling:

app.get('/file/:name', (req, res) => {
  const filePath = getFilePath(req.params.name);
  const stat = fs.statSync(filePath);
  const fileSize = stat.size;
  const rangeHeader = req.headers.range;

  if (!rangeHeader) {
    return res.status(200)
      .set('Content-Length', fileSize)
      .set('Accept-Ranges', 'bytes')
      .sendFile(filePath);
  }

  const parts = rangeHeader.replace('bytes=', '').split('-');
  const start = parseInt(parts[0], 10);
  const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;

  if (start >= fileSize || end >= fileSize || start > end) {
    return res.status(416)
      .set('Content-Range', 'bytes */' + fileSize)
      .end();
  }

  const chunkSize = end - start + 1;
  const stream = fs.createReadStream(filePath, {start, end});
  res.status(206)
    .set('Content-Range', 'bytes ' + start + '-' + end + '/' + fileSize)
    .set('Content-Length', chunkSize)
    .set('Accept-Ranges', 'bytes');
  stream.pipe(res);
});

416 vs 206 vs 200

CodeMeaningRange header
206Partial ContentValid range, server returns the requested bytes
200OKNo Range header, or server ignores it and returns full content
416Range Not SatisfiableRange is out of bounds or invalid

Frequently asked questions

What does HTTP 416 mean?

The byte range specified in the Range request header cannot be satisfied because it falls outside the bounds of the resource. Check the Content-Range: bytes */N header in the 416 response to learn the actual resource size.

How do I fix a 416 error as a client?

Use the actual file size from the Content-Range header to recalculate the valid range. If doing a resumable download and the file changed, restart the download from the beginning with a new HEAD request to get the current size and ETag.

What does Content-Range: bytes */524288000 mean?

The * means the range portion is unknown or unsatisfiable (the server is not specifying a range). The number after the slash is the actual total size of the resource in bytes. In this example, the resource is 524,288,000 bytes (500MB).

Do all servers support byte ranges?

Most production web servers (nginx, Apache, Caddy, IIS) support byte ranges automatically for static files. Application servers serving dynamic content may not implement range support. A server indicates range support with the Accept-Ranges: bytes response header. If the header is absent or Accept-Ranges: none, the server does not support ranges and clients should not send Range headers.

Related guides

HTTP 206 Partial Content · HTTP 412 Precondition Failed

Standards reference

Definitions from the IANA HTTP Status Code Registry and RFC 9110 §15.5.17. Human-readable guidance by ErrorLookup. · HTTP 416 quick reference →