What are Byte Range requests?
Introduction
Byte range requests are a feature of HTTP. They allow clients to request only a portion of a resource; these requests are particularly useful when clients want to resume a paused (or a broken) download, or when they use a media player that supports random access (i.e. seeking to a specific point in a video without having to buffer the portion before said point).
The main benefit of byte range requests is therefore bandwidth conservation. When a user wants to see only a part of a video, it makes sense to only download the part that is needed and skip what is not. Similarly in PDF files, if we are interested in only a specific page (that is part of a larger PDF file), a byte-range-request-aware client can request that page explicitly and load the remaining of the document later, if needed.
In some cases, byte range requests could also be used to speed up transfers: if a device supports multihoming (that is, it can connect to multiple networks simultaneously), parts of the file could be downloaded over multiple networks and reassembled client-side. An example of this would be simultaneous downloads of a single file through a WiFi network as well as through a 4G cellular network (on a mobile device).
How it works
To negotiate such requests, the server must advertise its willingness to serve partial content. This is communicated with the Accept-Ranges
header field inside an HTTP response. This field must be set and its value must be some other than none
. If this is the case, the client can request a specific part of the resource with Range
request header in which it specifies the range of requested bytes.
If the requested range is valid, the server sends the partial content and sets HTTP response code to 206 Partial Content
while also setting the Content-Range
header to the range of the bytes that are being sent.
If the requested range is invalid, the server responds with a 416 Requested Range Not Satisfiable
status code.
Examples
The following examples use command line HTTP client cURL for sending and receiving requests.
A simple request
Let's send a request to a server to test whether it supports byte range requests. We run the curl
command with flag -I
which shows the response headers but omits the response body.
curl -I https://www.w3.org/Protocols/
HTTP/2 200
date: Fri, 11 Mar 2022 09:42:31 GMT
content-location: Overview.html
last-modified: Wed, 11 Jun 2014 14:21:46 GMT
etag: "6a1b-4fb902a09f280"
accept-ranges: bytes
content-length: 27163
cache-control: max-age=21600
expires: Fri, 11 Mar 2022 15:42:31 GMT
vary: Accept-Encoding
content-type: text/html; charset=iso-8859-1
x-backend: ssl-mirrors
x-request-id: 622b1987c230c64f
strict-transport-security: max-age=15552000; includeSubdomains; preload
content-security-policy: upgrade-insecure-requests
From the response headers we clearly see that the server supports byte range requests since the accept-ranges
response header is set to bytes
.
When the server does not support partial requests
When we query a server that does not support byte range requests, the accept-ranges
field is omitted.
curl -I https://bunny.net
HTTP/2 200
date: Fri, 11 Mar 2022 09:42:31 GMT
content-type: text/html; charset=utf-8
vary: Accept-Encoding
server: BunnyCDN-LJ1-818
cdn-pullzone: 224112
cdn-uid: 51eb4203-ff94-48c6-99a5-954f277b91de
cdn-requestcountrycode: SI
cdn-proxyver: 1.02
cdn-requestpullsuccess: True
cdn-requestpullcode: 200
cdn-cachedat: 03/06/2022 18:39:15
cdn-edgestorageid: 818
cdn-status: 200
cdn-requestid: b7cb2aa1fe165ca71f7351b4dd1f3e81
cdn-cache: HIT
cache-control: public, max-age=30
Sending a partial request
If we want to request only a certain range of the resource, we send an HTTP request where we set the Range
header field to the requested range. For instance, Range: bytes=0-8
would fetch only the first 9 bytes of the requested resource. (The -i
switch tells cURL to show both the headers and the body of the HTTP response while the -H "Range: bytes=0-8"
sets the request headers.)
curl -i https://www.w3.org/Protocols/ -H "Range: bytes=0-8"
HTTP/2 206
date: Fri, 11 Mar 2022 09:42:32 GMT
content-location: Overview.html
last-modified: Wed, 11 Jun 2014 14:21:46 GMT
etag: "6a1b-4fb902a09f280"
accept-ranges: bytes
content-length: 9
cache-control: max-age=21600
expires: Fri, 11 Mar 2022 15:42:32 GMT
vary: Accept-Encoding
content-range: bytes 0-8/27163
content-type: text/html; charset=iso-8859-1
x-backend: ssl-mirrors
x-request-id: 622b19884f46984f
strict-transport-security: max-age=15552000; includeSubdomains; preload
content-security-policy: upgrade-insecure-requests
<!DOCTYPE
The server set the response code to 206
and the response now contains only the first 9 bytes of the requested resource, which is the initial string of the HTML document: <!DOCTYPE
.
Requesting an invalid range
If we request an invalid range, the server responds with code 416
as follows.
curl -I https://www.w3.org/Protocols/ -H "Range: bytes=10-0"
HTTP/2 200
date: Fri, 11 Mar 2022 09:42:32 GMT
content-location: Overview.html
last-modified: Wed, 11 Jun 2014 14:21:46 GMT
etag: "6a1b-4fb902a09f280"
accept-ranges: bytes
content-length: 27163
cache-control: max-age=21600
expires: Fri, 11 Mar 2022 15:42:32 GMT
vary: Accept-Encoding
content-type: text/html; charset=iso-8859-1
x-backend: ssl-mirrors
x-request-id: 622b19889fcde0b6
strict-transport-security: max-age=15552000; includeSubdomains; preload
content-security-policy: upgrade-insecure-requests
Oh, no... A more correct response from the server would be the following:
curl -i https://lem.fri.uni-lj.si -H "Range: bytes=8-0"
HTTP/2 416
server: nginx
date: Fri, 11 Mar 2022 09:42:32 GMT
content-type: text/html; charset=UTF-8
content-length: 206
strict-transport-security: max-age=31536000; includeSubDomains
content-range: bytes */6599
<html>
<head><title>416 Requested Range Not Satisfiable</title></head>
<body bgcolor="white">
<center><h1>416 Requested Range Not Satisfiable</h1></center>
<hr><center>nginx</center>
</body>
</html>
Unfortunately, not every server implements the functionality in its entirety. Simply put: additional care may be required when trying to use HTTP BRRs (Byte Range Requests).