Range requests let a client ask for part of a representation. Media players, resumable download tools, and diagnostic clients use them, but a server that advertises or returns ranges incorrectly can produce silent corruption instead of a clean failure.
Establish the complete size
Use an immutable test download of at least 16 bytes. Run the fragments in the same disposable shell session so url and size remain available. Their explicit set -eu stops after a failed transfer, size check, or comparison; inspect the HTTP status and range fields separately. Save the complete body, headers, and validator in that temporary working directory:
set -eu
url=https://www.example.test/downloads/archive.tar.gz
curl -fsS -H 'Accept-Encoding: identity' -D full.headers -o full.body "$url"
size=$(wc -c < full.body)
test "$size" -ge 16
awk 'tolower($1) ~ /^(content-length:|content-encoding:|etag:|last-modified:|accept-ranges:)$/ { print }' full.headers
Check that the full response is 200 and has no unexpected content coding. The commands request the identity representation and do not use automatic decompression; byte ranges apply to the selected encoded representation, so changing encoding invalidates the comparison. Content-Length may be absent, which is why the complete body supplies the size.
Accept-Ranges: bytes is an explicit signal, but absence of the header does not by itself prove that range requests are unsupported. Exercise the request.
Request a known interval
Ask for the first 16 bytes and keep both headers and body:
set -eu
curl -sS -D range.headers -o range.body \
-H 'Accept-Encoding: identity' -H 'Range: bytes=0-15' "$url"
wc -c range.body
head -c 16 full.body > expected.body
cmp expected.body range.body
awk 'tolower($1) ~ /^(content-range:|content-length:|etag:)$/ || /^HTTP\// { print }' range.headers
A successful byte-range response uses 206 Partial Content. Content-Range should identify 0-15 and the complete representation length, while the body should contain exactly 16 bytes.
The byte comparison deliberately tests the partial-body case. A valid full 200 larger than 16 bytes will fail it and stop this shell session; inspect the saved headers before calling that corruption. Conversely, a complete 16-byte file can pass cmp even with 200, so status and Content-Range still need checking.
Test an impossible range
Start at the complete byte count: for a file of size N, the last valid offset is N−1. Unlike a fixed large constant, this is outside the known representation:
set -eu
curl -sS -D - -o /dev/null \
-H 'Accept-Encoding: identity' -H "Range: bytes=${size}-" "$url"
When the server rejects an unsatisfiable byte range with 416 Range Not Satisfiable, it should include Content-Range, for example bytes */12345, to report the current size. A 200 response may be valid when the server ignores the range, but it must return the complete representation rather than a mislabeled fragment.
Keep validators in the story
Resumed downloads can use If-Range with a strong ETag. A weak ETag is forbidden; an HTTP date is allowed only when no ETag is available and the date qualifies as a strong validator. If the representation changed, the safe response is the full current representation, not bytes spliced from two versions. Test this behavior before calling a download resumable.
Sources
- RFC 9110 — HTTP Semantics, range units, partial responses, and
Content-Range, checked 2026-07-06. - curl manual,
--rangeand header options, checked 2026-07-06.