HTTP Range Request Reference

Range, Content-Range, Accept-Ranges headers with byte-range syntax and 206 flow.

Reference for HTTP byte-range request headers with multi-part range syntax and server support notes, plus a live parser that resolves each range against a resource size and shows the 206 or 416 outcome. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

Are byte ranges inclusive?

Yes. Both the first and last positions are inclusive and zero-based, so bytes=0-499 requests the first 500 bytes. The number of bytes returned is last − first + 1.

Fetching part of a resource

HTTP range requests let a client download just a slice of a resource — the foundation of resumable downloads, video seeking and parallel chunked transfers. The client asks with a Range header, the server answers with 206 Partial Content and a Content-Range, and advertises support up front with Accept-Ranges. This reference covers the full header set with a parser that resolves any Range value against a resource size.

What range requests enable

Range requests are the mechanism behind several everyday browser behaviours:

  • Video/audio seeking — when you jump to a timestamp in a <video>, the browser sends a Range request for the bytes at that time offset. The server responds with 206 containing just those bytes.
  • Resumable downloads — a download manager notes how many bytes it received before a connection drop, then resumes with Range: bytes=<offset>-.
  • Parallel chunked transfers — some download managers open multiple connections each fetching a different range, then reassemble them.
  • PDF preview — PDF readers often fetch just the first few kilobytes (the header), then request additional pages on demand.

The server advertises support with Accept-Ranges: bytes on any regular 200 response. Accept-Ranges: none means range requests are not supported.

The range syntax in full

A Range header names one or more byte ranges in the form bytes=spec[, spec...]:

Range: bytes=0-499          (first 500 bytes; positions 0 through 499 inclusive)
Range: bytes=500-           (open-ended: position 500 to the last byte)
Range: bytes=-200           (suffix: the final 200 bytes)
Range: bytes=0-499, 1000-1499   (two disjoint ranges)

Positions are zero-based and inclusive. For a 1000-byte file:

  • bytes=0-499 → bytes 0–499 (500 bytes)
  • bytes=500- → bytes 500–999 (500 bytes)
  • bytes=-200 → bytes 800–999 (200 bytes)
  • bytes=1000- → invalid, first position equals file size → 416

Server responses

Single range — 206 Partial Content:

HTTP/1.1 206 Partial Content
Content-Range: bytes 0-499/1000
Content-Length: 500
Content-Type: video/mp4

[500 bytes of data]

Multiple ranges — 206 with multipart body:

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

--boundary42
Content-Type: video/mp4
Content-Range: bytes 0-499/1000

[bytes 0499]
--boundary42
Content-Type: video/mp4
Content-Range: bytes 1000-1499/1000

[bytes 10001499]
--boundary42--

Out-of-range — 416 Range Not Satisfiable:

HTTP/1.1 416 Range Not Satisfiable
Content-Range: bytes */1000

The Content-Range: bytes */size in a 416 tells the client the actual resource size so it can correct its request.

Conditional range requests with If-Range

When resuming a download, the client should check that the resource has not changed since it started — otherwise the resumed bytes would not match the original bytes. Use If-Range with the ETag (or Last-Modified) of the resource:

Range: bytes=500-
If-Range: "abc123etag"

If the ETag still matches, the server sends 206 with the requested range. If the resource has changed, the server ignores the Range and returns the full resource as 200, so the client can start fresh. Without If-Range, the client might receive bytes that belong to a different version of the file.

Tips and gotchas

  • Content-Length on a 206 is the size of the returned slice, not the total file size. The total is in the denominator of Content-Range: bytes first-last/total.
  • Only the bytes unit is universally supported; servers ignore or reject unknown range units.
  • Multiple ranges trigger a multipart/byteranges response, which adds framing overhead — for small numbers of ranges it is often more efficient to fetch them as a single wider range.
  • A server that supports Accept-Ranges: bytes but receives an invalid Range value (such as Range: bytes=abc) should respond with 200 and the full resource.