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.

A stable encoded representation of N bytes supplies the reference size. A ruler selects inclusive offsets 0 through 15, exactly 16 bytes. Outcomes distinguish matching 206 bytes and Content-Range, complete 200 when Range is ignored, and rejected unsatisfiable ranges starting at offset N with 416 and bytes */N. Strong If-Range validators prevent splicing different versions. Weak ETags are forbidden; a strong HTTP date qualifies only when no ETag exists. Changed representation requires a complete current body.
Keep the representation and content coding stable. Match interval, total size and actual body bytes; a complete 200 can be valid when Range is ignored. Resume only with an appropriate strong validator.

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