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 aRangerequest for the bytes at that time offset. The server responds with206containing 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 0–499]
--boundary42
Content-Type: video/mp4
Content-Range: bytes 1000-1499/1000
[bytes 1000–1499]
--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-Lengthon a206is the size of the returned slice, not the total file size. The total is in the denominator ofContent-Range: bytes first-last/total.- Only the
bytesunit is universally supported; servers ignore or reject unknown range units. - Multiple ranges trigger a
multipart/byterangesresponse, 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: bytesbut receives an invalidRangevalue (such asRange: bytes=abc) should respond with200and the full resource.