HTTP Range Requests

Range requests allow a client to request a specific portion of a resource rather than the entire representation. A video player seeking to a timestamp, a download manager resuming an interrupted transfer, or a PDF viewer loading a single page all rely on range requests to fetch only the bytes they need.

Range requests are part of HTTP semantics. The mechanism works through the Range request header, the Content-Range response header, and the 206 status code.

Usage

Range requests are not universally supported. A server advertises support by including the Accept-Ranges response header with a value indicating the supported range unit. Currently bytes is the only range unit registered with IANA and in widespread use.

Accept-Ranges: bytes

A value of none explicitly declares range requests unsupported. When Accept-Ranges is absent, the client has no guarantee either way.

When a server supports range requests and receives a valid Range header, the response uses the 206 status code with the requested portion of the resource. If the requested range falls outside the bounds of the resource, the server responds with 416. A server is also free to ignore the Range header entirely and return 200 with the full resource.

Checking for support

Before requesting partial content, a client sends an HTTP HEAD request to check whether the server supports range requests. The Accept-Ranges header in the response confirms support, and Content-Length reveals the total size of the resource.

Request

HEAD /large-image.jpg HTTP/1.1
Host: www.example.re

Response

HTTP/1.1 200 OK
Content-Type: image/jpeg
Content-Length: 25000
Accept-Ranges: bytes

The server accepts byte range requests and the total file size is 25,000 bytes.

Single range requests

A single range request retrieves one contiguous byte sequence. The Range header specifies the start and end byte positions, both inclusive.

Request

GET /large-image.jpg HTTP/1.1
Host: www.example.re
Range: bytes=0-999

Response

HTTP/1.1 206 Partial Content
Content-Type: image/jpeg
Content-Length: 1000
Content-Range: bytes 0-999/25000

(1000 bytes of image data)

Content-Length in range responses

The Content-Length header in a 206 response reflects the number of bytes in the message body (the partial content), not the total size of the resource. The total size appears after the slash in the Content-Range header.

The Range header supports three formats:

  • bytes=0-999 retrieves bytes 0 through 999 (the first 1,000 bytes)
  • bytes=5000- retrieves from byte 5,000 to the end of the resource
  • bytes=-500 retrieves the last 500 bytes

Multipart range requests

Multiple byte ranges are requested in a single Range header by separating ranges with commas.

Range: bytes=0-999, 2000-2499, 5000-5999

The server responds with Content-Type: multipart/byteranges and a boundary string. Each part in the message body includes its own Content-Type and Content-Range headers.

Request

GET /large-image.jpg HTTP/1.1
Host: www.example.re
Range: bytes=0-999, 2000-2499, 5000-5999

Response

HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=range_divider
Content-Length: 2712

--range_divider
Content-Type: image/jpeg
Content-Range: bytes 0-999/25000

(1000 bytes of image data)

--range_divider
Content-Type: image/jpeg
Content-Range: bytes 2000-2499/25000

(500 bytes of image data)

--range_divider
Content-Type: image/jpeg
Content-Range: bytes 5000-5999/25000

(1000 bytes of image data)
--range_divider--

The top-level Content-Type is multipart/byteranges with the boundary parameter. Each body part starts with a boundary delimiter and contains its own headers describing the specific byte range.

Merged ranges

A server is free to merge overlapping or adjacent ranges. Two ranges like bytes=0-500, 400-999 are coalesced into a single bytes 0-999 part. The response is valid as long as all requested bytes are covered, even if the number of parts differs from the number of ranges requested.

Unsatisfiable ranges

When the requested range falls entirely outside the resource boundaries, the server returns 416 Range Not Satisfiable. The response includes a Content-Range header with the total size of the resource so the client knows the valid range.

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

A range like bytes=30000-40000 on a 25,000-byte resource triggers this response.

Conditional range requests

Resuming a download introduces a risk: the resource on the server changed since the last partial transfer. The If-Range header addresses this by combining a conditional request with a range request in a single round-trip.

If-Range accepts either an ETag or a Last-Modified date as the validator.

Using an ETag

GET /large-image.jpg HTTP/1.1
Host: www.example.re
Range: bytes=1000-1999
If-Range: "abc123"

Using a Last-Modified date

GET /large-image.jpg HTTP/1.1
Host: www.example.re
Range: bytes=1000-1999
If-Range: Sun, 01 Jan 2023 12:00:00 GMT

If the validator matches, the server returns 206 with the requested range. If the resource changed (the validator no longer matches), the server ignores the Range header and returns 200 with the full resource. This avoids the extra round-trip separate If-Match and Range headers require.

Resumable downloads

Download managers and HTTP clients store the ETag or Last-Modified value from the initial response. On retry, the client sends If-Range with the stored validator and a Range starting from the last received byte. This pattern handles both the resume case (resource unchanged, 206) and the restart case (resource changed, 200) without needing an extra request.

Takeaway

Range requests enable partial content retrieval through the Range request header and the 206 response status. A single request retrieves one byte range. Multiple ranges arrive in a multipart/byteranges response. The If-Range header combines conditional validation with range selection, making resumable downloads safe against mid-transfer resource changes.

See also

Last updated: March 11, 2026