HTTP 206 Partial Content

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.

Debug HTTP 206 live
Analyze real 206 behavior — headers, caching, CORS, redirects
Open Inspector →

Try it (live endpoint)

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"
Try in playground

Meaning

The server is delivering only part of the resource in response to a Range header (used for resumable downloads and video streaming).

What it guarantees
  • The server accepted the request and produced a final response.
What it does NOT guarantee
  • The underlying business operation is correct across all downstream systems.
  • The response is cacheable unless headers explicitly allow it.

When to use this status

  • GET succeeds and returns a representation of the resource.
  • PUT/PATCH succeeds and returns an updated representation.
  • POST succeeds and returns an immediate result.

When NOT to use this status (common misuses)

Returning 200 for partial failures or errors embedded only in the body.
Clients and monitors treat it as success; failures become silent and harder to alert on.
Returning 200 for creation instead of 201 with Location.
Clients lose a reliable created-resource identifier; SDK behavior becomes inconsistent.
Returning 200 for async acceptance instead of 202.
Clients assume the work is complete and proceed incorrectly.

Critical headers that matter

Content-Type
Defines how clients parse the body.
Clients mis-parse payloads; SDKs and browsers apply wrong decoding.
Cache-Control
Controls cacheability and revalidation.
CDNs/browsers cache dynamic data or fail to cache static content.
ETag / Last-Modified
Enables conditional requests and revalidation.
Unnecessary bandwidth; poor cache consistency.

Tool interpretation

Browsers
Treats as success; caches/revalidates based on headers and validators.
API clients
Deserializes per Content-Type; conditional requests use validators when implemented.
Crawlers / SEO tools
Indexes depending on headers and canonical stability; caches behavior via validators and cache directives.
Uptime monitors
Typically marks success; advanced checks may flag header anomalies or latency.
CDNs / reverse proxies
Caches/revalidates based on Cache-Control, ETag, and Vary; compression and content-type affect behavior.

Inspector preview (read-only)

On this code, Inspector focuses on semantics, headers, and correctness warnings that commonly affect clients and caches.

Signals it will highlight
  • Status semantics vs method and body expectations
  • Header sanity (Content-Type, Cache-Control, Vary) and evidence completeness
Correctness warnings
No common correctness warnings are specific to this code.

Guided Lab outcome

  • Reproduce HTTP 206 Partial Content using a controlled endpoint and capture the full exchange.
  • Practice distinguishing status semantics from transport issues (redirects, caching, proxies).

Technical deep dive

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.

Real-world examples

Video streaming seek
A user clicks to 50% of a 100MB video. The player sends Range: bytes=50000000-. The server returns 206 with Content-Range: bytes 50000000-99999999/100000000 and streams the latter half. This avoids downloading the entire video just to skip ahead.
Resumable file download
A 2GB download fails at 1.5GB. The download manager restarts with Range: bytes=1500000000-. The server verifies the file hasn't changed (via If-Range) and returns 206 with the remaining 500MB, saving 75% of the bandwidth.
PDF page-by-page loading
A PDF viewer loads a 200-page document. It requests the cross-reference table at the end of the file first (Range: bytes=-1000), then fetches individual page data as the user scrolls, never downloading the entire file.

Framework behavior

Express.js (Node)
Express static middleware handles Range automatically for static files. For custom endpoints: check req.headers.range, parse the byte range, set Content-Range and 206 status. The 'range-parser' npm package helps parse Range headers.
Django / DRF (Python)
Django's FileResponse supports Range requests when the file backend supports seeking. For custom views, use django-ranged-response or manually parse Range headers and return StreamingHttpResponse with status=206.
Spring Boot (Java)
Spring Boot's ResourceHttpRequestHandler handles Range for static resources automatically. For @ResponseBody, use ResourceRegion: ResponseEntity<ResourceRegion> with status 206 and Content-Range header.
FastAPI (Python)
FastAPI with FileResponse handles Range via Starlette's built-in support. For custom streaming, use StreamingResponse with status_code=206 and manually set Content-Range headers.

Debugging guide

  1. Check if the server sends Accept-Ranges: bytes in 200 responses — absence means range requests aren't supported
  2. Verify Content-Range format is 'bytes START-END/TOTAL' — incorrect format breaks resume logic
  3. If range requests return 200 instead of 206, the server may not support ranges or the Range header is malformed
  4. Test with: curl -H 'Range: bytes=0-99' -v https://example.com/file.mp4 — should return 206
  5. If video seeking doesn't work, check that the CDN/proxy isn't stripping Range headers or caching full responses

Code snippets

Node.js
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);
  }
});
Python
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'
        )
Java (Spring)
@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));
}
Go
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
}

FAQ

How does 206 Partial Content enable video seeking?
When you click ahead in a video, the player sends a Range request for bytes starting at the new position. The server returns 206 with just those bytes. HTML5 video players handle this automatically — they issue Range requests as the user seeks. Without Range support, the browser would need to download everything from the start to the seek point.
What happens if the requested range is invalid?
The server returns 416 Range Not Satisfiable with a Content-Range header showing the actual resource size: Content-Range: bytes */5000. This tells the client the valid range. Common cause: requesting bytes beyond the file size after a file was truncated or replaced.
Do CDNs support 206 Partial Content?
Yes, major CDNs (CloudFront, Cloudflare, Fastly) support Range requests. They either forward Range to the origin or serve ranges from cached full responses. Some CDNs fetch the full resource on first request and serve subsequent Range requests from cache. Check your CDN's documentation for range request caching behavior.
Can I use Range requests with API endpoints?
While technically possible, Range is designed for byte-level access to static resources. For paginating API results, use query parameters (page, limit, offset) or cursor-based pagination. Using Range for API pagination is non-standard and confuses clients that expect byte ranges.

Client expectation contract

Client can assume
  • A final HTTP response was produced and processed by the server.
Client must NOT assume
  • The change is durable across all downstream systems.
Retry behavior
Retries are generally unnecessary; treat as final unless domain rules require revalidation.
Monitoring classification
Success
Use payload and header checks to avoid false positives; cacheability depends on Cache-Control/ETag/Vary.

Related status codes

205 Reset Content
The server successfully processed the request, asks that the requester reset its document view (e.g., clear a form for new input), and is not returning any content.
207 Multi-Status
Provides status for multiple independent operations (WebDAV). The response body contains XML with status information for each operation.

Explore more

Related guides
Related tools
Related utilities