HTTP

Header

Accept-Ranges Header

Learn how the Accept-Ranges header tells clients whether your server supports partial content requests (byte ranges) for efficient downloads and streaming.

5 min read intermediate Try in Playground

TL;DR: Tells clients whether the server supports partial content requests (byte ranges). Enables resume downloads, video seeking, and efficient large file transfers.

What is Accept-Ranges?

The Accept-Ranges header tells clients whether the server supports partial content requests. It’s like a server saying “Yes, you can download this file in chunks” or “No, you must download the whole thing at once.”

This enables features like pause/resume downloads, video seeking, and efficient large file transfers.

How Accept-Ranges Works

Server indicates range support:

HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 10485760
Content-Type: video/mp4

[video content]
```text

**Client can then request partial content:**

```http
GET /video.mp4 HTTP/1.1
Host: cdn.example.com
Range: bytes=0-1048575

Server responds with partial content:

HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1048575/10485760
Content-Length: 1048576
Accept-Ranges: bytes

[first 1MB of video]
```http

## Syntax

```http
Accept-Ranges: bytes
Accept-Ranges: none

Values

  • bytes - Server supports byte-range requests
  • none - Server does not support range requests

Common Examples

Supporting Range Requests

HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Type: application/pdf
Content-Length: 5242880
```text

Server supports byte ranges for the PDF file.

### Not Supporting Range Requests

```http
HTTP/1.1 200 OK
Accept-Ranges: none
Content-Type: text/html

Server requires the entire resource to be downloaded.

Streaming Video

HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Type: video/mp4
Content-Length: 104857600
```text

Video can be streamed and seeked because byte ranges are supported.

## Real-World Scenarios

### Video Streaming Platform

```http
# Initial request
GET /videos/movie.mp4 HTTP/1.1
Host: streaming.example.com

# Server response
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Type: video/mp4
Content-Length: 524288000

# Client seeks to 5 minutes in
GET /videos/movie.mp4 HTTP/1.1
Host: streaming.example.com
Range: bytes=15728640-20971519

# Server sends that chunk
HTTP/1.1 206 Partial Content
Content-Range: bytes 15728640-20971519/524288000
Accept-Ranges: bytes

Download Manager with Resume

# Download interrupted at 50%
GET /large-file.zip HTTP/1.1
Host: downloads.example.com
Range: bytes=52428800-

# Server resumes from that point
HTTP/1.1 206 Partial Content
Content-Range: bytes 52428800-104857599/104857600
Accept-Ranges: bytes
Content-Length: 52428800
```text

### PDF Viewer Progressive Loading

```http
# Load first page quickly
GET /document.pdf HTTP/1.1
Host: docs.example.com
Range: bytes=0-102399

HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Range: bytes 0-102399/2097152

Server Implementation

Express.js (Node.js)

const express = require('express')
const fs = require('fs')
const app = express()

app.get('/video/:id', (req, res) => {
  const videoPath = `./videos/${req.params.id}.mp4`
  const stat = fs.statSync(videoPath)
  const fileSize = stat.size
  const range = req.headers.range

  // Always indicate range support
  res.setHeader('Accept-Ranges', 'bytes')

  if (range) {
    const parts = range.replace(/bytes=/, '').split('-')
    const start = parseInt(parts[0], 10)
    const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1
    const chunkSize = end - start + 1
    const file = fs.createReadStream(videoPath, { start, end })

    res.status(206)
    res.setHeader('Content-Range', `bytes ${start}-${end}/${fileSize}`)
    res.setHeader('Content-Length', chunkSize)
    res.setHeader('Content-Type', 'video/mp4')

    file.pipe(res)
  } else {
    res.setHeader('Content-Length', fileSize)
    res.setHeader('Content-Type', 'video/mp4')
    fs.createReadStream(videoPath).pipe(res)
  }
})
```nginx

### Nginx Configuration

```nginx
server {
  listen 80;
  server_name cdn.example.com;

  location /videos/ {
    # Nginx automatically adds Accept-Ranges: bytes
    # and handles range requests for static files
    root /var/www;
  }

  location /api/ {
    # Disable range requests for API endpoints
    add_header Accept-Ranges none;
    proxy_pass http://backend;
  }
}

Python (Flask)

from flask import Flask, request, send_file, Response
import os

app = Flask(__name__)

@app.route('/download/<filename>')
def download_file(filename):
    file_path = f'./files/{filename}'
    file_size = os.path.getsize(file_path)

    range_header = request.headers.get('Range')

    if range_header:
        byte_range = range_header.replace('bytes=', '').split('-')
        start = int(byte_range[0])
        end = int(byte_range[1]) if byte_range[1] else file_size - 1

        with open(file_path, 'rb') as f:
            f.seek(start)
            data = f.read(end - start + 1)

        response = Response(data, 206, mimetype='application/octet-stream')
        response.headers.add('Content-Range', f'bytes {start}-{end}/{file_size}')
        response.headers.add('Accept-Ranges', 'bytes')
        response.headers.add('Content-Length', len(data))

        return response
    else:
        response = send_file(file_path)
        response.headers.add('Accept-Ranges', 'bytes')
        return response
```text

## Best Practices

### For Servers

**1. Enable range support for large files**

```http
# ✅ Good for videos, downloads, large PDFs
Accept-Ranges: bytes

# ❌ Avoid for dynamic content
Accept-Ranges: none

2. Always include Accept-Ranges header

// Even when not supporting ranges, be explicit
res.setHeader('Accept-Ranges', 'none')
```javascript

**3. Validate range requests**

```javascript
if (range) {
  const parts = range.replace(/bytes=/, '').split('-')
  const start = parseInt(parts[0], 10)
  const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1

  // Validate ranges
  if (start >= fileSize || end >= fileSize || start > end) {
    return res.status(416).send('Range Not Satisfiable')
  }
}

4. Set proper cache headers

Accept-Ranges: bytes
ETag: "abc123"
Cache-Control: public, max-age=3600
```text

### For Clients

**1. Check for range support before requesting ranges**

```javascript
// First, check if server supports ranges
fetch('/video.mp4', { method: 'HEAD' }).then((res) => {
  if (res.headers.get('Accept-Ranges') === 'bytes') {
    // Safe to request ranges
    return fetch('/video.mp4', {
      headers: { Range: 'bytes=0-1048575' }
    })
  }
})

2. Handle both 200 and 206 responses

fetch('/file.zip', {
  headers: { Range: 'bytes=1000000-' }
}).then((res) => {
  if (res.status === 206) {
    // Partial content received
    console.log('Resuming download')
  } else if (res.status === 200) {
    // Full content (server doesn't support ranges)
    console.log('Downloading full file')
  }
})
```text

## Common Use Cases

### Video Players

```http
Accept-Ranges: bytes

Allows seeking to any point in the video without downloading the entire file.

Download Managers

Accept-Ranges: bytes
```text

Enables pause/resume functionality and parallel chunk downloads.

### PDF Viewers

```http
Accept-Ranges: bytes

Progressive loading of large documents for better user experience.

CDN Distribution

Accept-Ranges: bytes
```text

Efficient distribution of large files across network segments.

## When to Disable Range Requests

### Dynamic Content

```http
Accept-Ranges: none

Content generated on-the-fly that can’t be easily chunked.

Compressed Responses

Accept-Ranges: none
Content-Encoding: gzip
```text

Gzipped content typically shouldn't support ranges.

### Security-Sensitive Content

```http
Accept-Ranges: none

When partial access could expose sensitive information.

Testing Accept-Ranges

Using curl

# Check if ranges are supported
curl -I https://cdn.example.com/video.mp4

# Request first 1000 bytes
curl -H "Range: bytes=0-999" https://cdn.example.com/video.mp4

# Request from byte 1000 to end
curl -H "Range: bytes=1000-" https://cdn.example.com/video.mp4

# Request last 1000 bytes
curl -H "Range: bytes=-1000" https://cdn.example.com/video.mp4
```javascript

### Using JavaScript

```javascript
// Check range support
const response = await fetch('/video.mp4', { method: 'HEAD' })
console.log(response.headers.get('Accept-Ranges'))

// Request partial content
const partial = await fetch('/video.mp4', {
  headers: {
    Range: 'bytes=0-1048575'
  }
})
console.log(partial.status) // Should be 206 if supported
  • Range - Client requests specific byte ranges
  • Content-Range - Server indicates which bytes are being sent
  • Content-Length - Total size of the content
  • ETag - Resource version for conditional range requests

Frequently Asked Questions

What is Accept-Ranges?

Accept-Ranges indicates whether the server supports range requests. "bytes" means ranges are supported; "none" means they are not. Absence implies possible support.

Why is Accept-Ranges important?

It tells clients if they can resume downloads or request partial content. Video players and download managers check this before attempting range requests.

What does Accept-Ranges: none mean?

The server explicitly does not support range requests. Clients should not send Range headers and must download the complete resource.

How do I enable Accept-Ranges on my server?

Most servers enable it by default for static files. For dynamic content, you must implement range handling in your application code and set the header explicitly.

Keep Learning