The direct check is curl -sI https://example.com/big.iso | grep -i 'accept-ranges' — no Accept-Ranges: bytes in the response means the server can't resume anything, and -C - won't save you. Skip that check and you can hit curl's ugliest failure mode: it exits with code 0, prints nothing alarming, and leaves you a file that's silently truncated. This walks through resuming properly, catching that failure, retrying, and confirming the result is actually whole.
1. Can the server resume downloads at all?
$ clai check if the server supports resuming downloads→ curl -sI https://example.com/big.iso | grep -i 'accept-ranges'
No Accept-Ranges: bytes in the response means resuming is impossible in principle, and -C - isn't going to change that. Thirty seconds of checking saves an hour of pain when a big download drops near the end.
2. Resuming a normal download
$ clai resume this interrupted download→ curl -C - -O https://example.com/big.iso
-C - tells curl to figure out the offset itself from the local file's size. -O saves it under the name from the URL. If the file's already complete, curl just says there's nothing left to fetch.
3. The trap: the server doesn't support Range
$ clai download the file and verify it came through complete→ curl -C - -o part.bin http://example.com/big.bin && cmp -s part.bin original.bin && echo intact || echo TRUNCATED
This is the line the whole article exists for. When a server ignores Range, curl still exits 0 — no error, no warning — and the file stays cut off exactly where it stopped. Tested against a real server: 51200 bytes landed instead of 200000, with not a single error message anywhere. Never trust a resumed download without checking it — by size, by cmp, or by checksum.
4. Retrying until the download finishes
$ clai download this file with retries until it fully downloads→ curl --retry 10 --retry-delay 5 --retry-all-errors -C - -O https://example.com/big.iso
--retry covers network failures, --retry-all-errors extends that to 5xx responses too. Combined with -C -, every retry continues from where the last attempt stopped instead of starting over.
5. Capping the speed so you don't saturate the link
$ clai download the file but cap it at one megabyte per second→ curl --limit-rate 1M -C - -O https://example.com/big.iso
Useful on a box where something else still needs the bandwidth. Accepts k, M, and G suffixes.
6. Verifying you got the right file
$ clai verify the checksum of the file you just downloaded→ curl -sO https://example.com/big.iso.sha256 && sha256sum -c big.iso.sha256
The last step of any resumed download. A matching checksum is the only real proof that a file stitched back together from pieces is intact — size can match while the content doesn't.
Gotchas
- curl never tells you a resume failed. Exit code 0, file truncated. Checking after the fact is the only real defense.
- If the file changed on the server mid-resume, you get garbage. Bytes from the old version and the new one end up spliced into one file. When in doubt, delete the partial file and start clean.
-C -plus an-opointing at a leftover file is dangerous. curl resumes whatever's already at that path — the filename is the only thing it checks, not where the bytes came from.
Related questions
wget or curl? wget -c does the same resume logic and is simpler for a single file. curl is more flexible in scripts and shows up on boxes where wget never got installed.
How do I resume a download over ssh? rsync --partial --progress --append-verify — it resumes and verifies the part you already have.
What does "bad download resume" mean? curl couldn't pick up from the right offset — the server doesn't support Range, or the file changed underneath it. Start over from zero.
See also
Stop guessing which curl flags a resumed download needs — describe it and CliAI writes the command. Install it in one line.