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.
Quick reference
| Code | 416 |
|---|---|
| Name | Range Not Satisfiable |
| Category | 4xx Client Error |
| Specification | RFC 9110 §15.5.17 |
| Required response header | Content-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 header | Meaning | Valid for 1000-byte file? |
|---|---|---|
bytes=0-499 | First 500 bytes | Yes → 206 |
bytes=500-999 | Last 500 bytes | Yes → 206 |
bytes=-500 | Last 500 bytes (suffix) | Yes → 206 |
bytes=500- | From byte 500 to end | Yes → 206 |
bytes=1000-1499 | Starts at byte 1000 | No → 416 (file ends at 999) |
bytes=200-100 | Start greater than end | No → 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
| Code | Meaning | Range header |
|---|---|---|
| 206 | Partial Content | Valid range, server returns the requested bytes |
| 200 | OK | No Range header, or server ignores it and returns full content |
| 416 | Range Not Satisfiable | Range 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
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 →