206 Partial Content

The server fulfilled a range request and returned only the specified portion of the resource.

Quick Reference

Category2xx Success
RFCRFC 9110, Section 15.3.7
TriggerRequest includes a Range header and the server supports range requests
CacheableYes — partial responses are cacheable
Required headersContent-Range for single range; Content-Type: multipart/byteranges for multiple ranges

What 206 Partial Content Means

HTTP 206 Partial Content is the successful response to a range request. When a client includes a Range header in a GET request, it is asking for only part of the resource. If the server supports range requests for that resource, it responds with 206 and the requested bytes.

206 enables several important use cases: video streaming (seeking to a timestamp), resumable downloads (continuing after an interrupted transfer), parallel downloads (fetching different segments simultaneously), and PDF viewers (loading individual pages on demand). Without range requests, all of these would require downloading the entire resource before use.

A server signals that it supports range requests by including Accept-Ranges: bytes in any response to that resource. A value of Accept-Ranges: none means range requests are not supported and clients should not try.

Range Request Syntax

The Range header uses byte ranges with inclusive start and end positions. Byte numbering is zero-based:

# First 1000 bytes (bytes 0 through 999)
Range: bytes=0-999

# Bytes 5000 through 9999
Range: bytes=5000-9999

# Last 500 bytes (suffix range)
Range: bytes=-500

# From byte 10000 to end of file
Range: bytes=10000-

# Multiple ranges (less common)
Range: bytes=0-499, 1000-1499

The server response for a single range includes a Content-Range header that describes which bytes are included and the total size:

HTTP/1.1 206 Partial Content
Content-Range: bytes 5000-9999/50000
Content-Length: 5000
Content-Type: video/mp4

[5000 bytes of video data]

The format is bytes start-end/total. If the total size is unknown (streaming), a * replaces the total: bytes 0-999/*.

Multi-Range Responses

When a request specifies multiple ranges, the server can return all of them in a single 206 response using multipart/byteranges content type:

HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES

--THIS_STRING_SEPARATES
Content-Type: video/mp4
Content-Range: bytes 0-499/10000

[bytes 0-499]
--THIS_STRING_SEPARATES
Content-Type: video/mp4
Content-Range: bytes 1000-1499/10000

[bytes 1000-1499]
--THIS_STRING_SEPARATES--

Multi-range requests are rarely used by video players — they prefer sequential single-range fetches for buffering. Multi-range is more common in PDF viewers that load specific page sections on demand.

Resumable Downloads

The classic use of 206 is resuming an interrupted download. The client tracks how many bytes it has received. After a reconnect, it sends a range request for the remaining bytes:

# Client has received 50,000 bytes of a 200,000-byte file
GET /files/large-archive.zip HTTP/1.1
Range: bytes=50000-

HTTP/1.1 206 Partial Content
Content-Range: bytes 50000-199999/200000
Content-Length: 150000

[remaining 150,000 bytes]

For resumable downloads to work correctly, the server must also validate that the file has not changed between the initial download and the resume. The client should send If-Range with the ETag or Last-Modified value from the original response. If the resource has changed, the server returns 200 with the full resource instead of 206:

GET /files/large-archive.zip HTTP/1.1
Range: bytes=50000-
If-Range: "abc123etag"

# If ETag still matches: 206 Partial Content
# If ETag has changed: 200 OK (full response)

Video Streaming

HTML5 video elements use range requests to implement seeking. When a user scrubs to a timestamp, the browser calculates the approximate byte offset and sends a range request for that position. Servers hosting video files must support range requests for seeking to work.

Nginx serves range requests for static files by default. No configuration is needed — any GET to a static file that includes a Range header will receive a 206 response if the range is valid. Apache also supports range requests for static files by default.

When proxying to an application server, the proxy must pass range requests through and must not buffer the entire upstream response before forwarding the partial content. Nginx’s proxy_pass handles this correctly for most configurations.

Implementing Range Requests in Node.js

const fs = require('fs');
const path = require('path');

app.get('/files/:filename', (req, res) => {
  const filePath = path.join(__dirname, 'files', req.params.filename);
  const stat = fs.statSync(filePath);
  const fileSize = stat.size;
  const rangeHeader = req.headers['range'];

  if (rangeHeader) {
    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) {
      res.status(416).set('Content-Range', `bytes */${fileSize}`).end();
      return;
    }

    const chunkSize = end - start + 1;
    const fileStream = fs.createReadStream(filePath, { start, end });

    res.status(206).set({
      'Content-Range': `bytes ${start}-${end}/${fileSize}`,
      'Accept-Ranges': 'bytes',
      'Content-Length': chunkSize,
      'Content-Type': 'video/mp4'
    });

    fileStream.pipe(res);
  } else {
    res.set({
      'Content-Length': fileSize,
      'Content-Type': 'video/mp4',
      'Accept-Ranges': 'bytes'
    });
    fs.createReadStream(filePath).pipe(res);
  }
});

206 vs Related Status Codes

CodeMeaningWhen returned
200 OKFull resourceNo Range header; or Range header but server ignores it
206 Partial ContentRequested byte rangeValid Range header; server supports range requests
416 Range Not SatisfiableRange outside file boundsRange header references bytes beyond the end of the file
412 Precondition FailedIf-Range condition failedIf-Range ETag or date does not match current resource

Frequently Asked Questions

What happens if a server ignores the Range header?

The server returns 200 OK with the full resource. This is valid — servers are not required to support range requests. However, video seeking and resumable downloads will not work. The client can detect range support via the Accept-Ranges: bytes header in the initial response.

Is 206 cacheable?

Yes. Caches can store partial responses and serve them for subsequent range requests that fall within the cached range. The cache must also store and honor the Content-Range header so it can determine whether a cached partial response satisfies a new range request.

How do parallel downloads work with 206?

Download managers split a file into segments and open multiple connections, each requesting a different byte range simultaneously. All connections receive 206 responses. The manager reassembles the segments in order. The total download time is bounded by the slowest segment rather than the sequential transfer time of the full file.

What is the If-Range header for?

If-Range prevents range resumption from producing a corrupted file if the resource changed between the initial download and the resume. It carries an ETag or Last-Modified value. If the server-side value matches, it returns 206. If it does not match (file changed), it returns 200 with the full new file.