HTTP 206 Partial Content indicates the server is delivering only part of a resource due to a Range header sent by the client. This enables resumable downloads, video streaming (seek to any position), and efficient large file transfers. When you scrub a YouTube video or resume a failed download, 206 Partial Content is the mechanism at work — the client requests bytes 5000000-5999999 and the server delivers exactly that chunk.
Response includes the status code, standard headers (including Content-Type), and a small diagnostic JSON body describing the request and returned status.
Simulator URL (copy in the app after load — not a normal link):
https://httpstatus.com/api/status/206
Example request:
curl -i "https://httpstatus.com/api/status/206"The server is delivering only part of the resource in response to a Range header (used for resumable downloads and video streaming).
On this code, Inspector focuses on semantics, headers, and correctness warnings that commonly affect clients and caches.
206 Partial Content (RFC 7233) is the response to a Range request. The client sends Range: bytes=0-999 and the server responds with 206, Content-Range: bytes 0-999/5000, and the requested byte range. For multipart ranges (Range: bytes=0-99,200-299), the server uses Content-Type: multipart/byteranges with each part having its own Content-Range. The server includes Accept-Ranges: bytes in regular 200 responses to advertise range support. Key: if the entity-tag or Last-Modified has changed since the client's If-Range value, the server returns the full 200 response instead of 206. The Content-Length in a 206 response is the length of the partial content, not the total resource.
const fs = require('fs');
const path = require('path');
app.get('/video/:name', (req, res) => {
const filePath = path.join(__dirname, 'videos', req.params.name);
const stat = fs.statSync(filePath);
const range = req.headers.range;
if (range) {
const [startStr, endStr] = range.replace('bytes=', '').split('-');
const start = parseInt(startStr, 10);
const end = endStr ? parseInt(endStr, 10) : stat.size - 1;
res.writeHead(206, {
'Content-Range': `bytes ${start}-${end}/${stat.size}`,
'Accept-Ranges': 'bytes',
'Content-Length': end - start + 1,
'Content-Type': 'video/mp4',
});
fs.createReadStream(filePath, { start, end }).pipe(res);
} else {
res.writeHead(200, { 'Content-Length': stat.size, 'Content-Type': 'video/mp4' });
fs.createReadStream(filePath).pipe(res);
}
});from fastapi.responses import StreamingResponse
import os
@app.get('/video/{name}')
async def stream_video(name: str, request: Request):
path = f'./videos/{name}'
file_size = os.path.getsize(path)
range_header = request.headers.get('range')
if range_header:
start, end = parse_range(range_header, file_size)
return StreamingResponse(
file_iterator(path, start, end),
status_code=206,
headers={
'Content-Range': f'bytes {start}-{end}/{file_size}',
'Accept-Ranges': 'bytes',
'Content-Length': str(end - start + 1),
},
media_type='video/mp4'
)@GetMapping("/video/{name}")
public ResponseEntity<ResourceRegion> streamVideo(
@PathVariable String name,
@RequestHeader HttpHeaders headers) {
Resource video = new FileSystemResource("videos/" + name);
long length = video.contentLength();
List<HttpRange> ranges = headers.getRange();
if (!ranges.isEmpty()) {
HttpRange range = ranges.get(0);
long start = range.getRangeStart(length);
long end = range.getRangeEnd(length);
ResourceRegion region = new ResourceRegion(video, start, end - start + 1);
return ResponseEntity.status(206)
.header("Accept-Ranges", "bytes")
.body(region);
}
return ResponseEntity.ok().body(new ResourceRegion(video, 0, length));
}func videoHandler(w http.ResponseWriter, r *http.Request) {
filePath := "./videos/" + chi.URLParam(r, "name")
http.ServeFile(w, r, filePath)
// Go's http.ServeFile automatically handles Range
// requests, returning 206 with Content-Range
}