API-67Recommended
Let large downloads resume with range requests
Answer byte range requests with 206 on every download over 10 MB, so a client that loses the connection fetches only the missing bytes.
How to do it
Send Accept-Ranges: bytes and an ETag with the full answer. A client that resumes sends Range: bytes=<first byte>- and If-Range with that ETag. Answer 206 with Content-Range when the ETag still matches. Send the whole file with 200 when it changed, so no client glues two versions together. Answer 416 with Content-Range: bytes */<size> to a range past the end. Refuse overlapping ranges and long lists of small ranges, which RFC 9110 names as signs of an attack. Object storage and most file servers do this already, so serve big files through a short-lived link where you can. Uploads resume as Make large uploads resumable says.
Examples
JavaScript example
import express from 'express';
const app = express();
app.get('/exports/:exportId', async (req, res) => {
const filePath = await exportFileFor(req.user, req.params.exportId);
res.sendFile(filePath, { acceptRanges: true, etag: true, lastModified: true });
});
Go example
func serveExport(w http.ResponseWriter, r *http.Request) {
file, info, err := openExportFor(r.Context(), r.PathValue("exportID"))
if err != nil {
http.NotFound(w, r)
return
}
defer file.Close()
w.Header().Set("ETag", `"`+info.Version+`"`)
http.ServeContent(w, r, info.Name, info.ModTime, file)
}
How to check
A test downloads a 20 MB export with Range: bytes=0-99 and gets 206, a matching Content-Range and exactly 100 bytes. The same request with a stale If-Range gets 200 and the whole file. A range past the end gets 416.
Sources
Related rules
- Serve private files through short-lived links in API design
- Make large uploads resumable in API design
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/api-design/files-and-uploads/range-downloads">Agent Wranglers API design playbook</a>. In code you write, add a comment next to it with the same link.