HTTP

Header

Content-Length

Learn how Content-Length specifies the body size in bytes. Essential for progress indicators, connection management, and chunked transfer decisions.

3 min read beginner Try in Playground

Content-Length

TL;DR: Specifies the exact size of the response body in bytes. Essential for proper HTTP parsing, connection reuse, and download progress indicators.

What is Content-Length?

Content-Length tells the browser exactly how many bytes the response body contains. It’s like a package label that says “this box weighs 5 pounds” - it helps the browser know when it has received the complete response.

This header is crucial for proper HTTP communication and enables features like progress bars and connection reuse.

How It Works

When a server sends a response, it counts the bytes in the body and includes that number:

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234

<!DOCTYPE html>
<html>
  <body>Hello World!</body>
</html>
```text

The browser reads exactly 1,234 bytes for the body, then knows the response is complete.

## Why It Matters

### Connection Reuse

Without `Content-Length`, browsers can't reuse connections:

```http
❌ No Content-Length = connection closes after each response
✅ With Content-Length = same connection handles multiple requests

Progress Indicators

Browsers use Content-Length to show download progress:

// Browser can show "Downloaded 500KB of 2MB (25%)"
fetch('/large-file.zip').then((response) => {
  const total = response.headers.get('Content-Length')
  // Show progress bar
})
```text

### Proper Parsing

Browser knows when response is complete:

```http
Content-Length: 100
[exactly 100 bytes of data]

Browser stops reading after 100 bytes.

Calculating Content-Length

The value must be the exact number of bytes in the response body:

Text Content

const body = 'Hello World!'
const bytes = Buffer.byteLength(body, 'utf8') // 12 bytes
// Content-Length: 12
```javascript

### JSON Response

```javascript
const data = { message: 'Hello' }
const body = JSON.stringify(data) // '{"message":"Hello"}'
const bytes = Buffer.byteLength(body, 'utf8') // 19 bytes
// Content-Length: 19

Binary Data

const imageBuffer = fs.readFileSync('image.jpg')
const bytes = imageBuffer.length // e.g., 45678 bytes
// Content-Length: 45678
```text

## When Content-Length is Required

### Fixed-Size Responses

```http
HTTP/1.1 200 OK
Content-Length: 500
Content-Type: application/json

{"users": [...]}

File Downloads

HTTP/1.1 200 OK
Content-Length: 2048576
Content-Type: application/pdf

[PDF file data]
```text

### POST Request Bodies

```http
POST /api/users HTTP/1.1
Content-Length: 45
Content-Type: application/json

{"name": "John", "email": "john@example.com"}

When Content-Length is Not Used

Chunked Transfer Encoding

For dynamic content where size is unknown:

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: text/html

5
Hello
6
 World
0

```text

### Streaming Responses

```http
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: text/plain

[data sent in chunks as it's generated]

Content-Length vs Transfer-Encoding

You cannot use both headers together:

❌ Invalid:
Content-Length: 1000
Transfer-Encoding: chunked

✅ Use one or the other:
Content-Length: 1000
OR
Transfer-Encoding: chunked
```text

## Common Issues

### Incorrect Length

```http
❌ Content-Length: 100
    [actually 150 bytes of data]

Result: Browser stops reading after 100 bytes, response appears truncated.

Missing Length

❌ No Content-Length header
    No Transfer-Encoding header
```javascript

**Result:** Browser doesn't know when response ends, connection must close.

### Encoding Mismatch

```javascript
❌ const body = 'Café' // 5 characters, but 6 bytes in UTF-8
   Content-Length: 5 // Wrong! Should be 6

Best Practices

1. Always include Content-Length for fixed-size responses:

Content-Length: 1234
```text

**2. Use chunked encoding for dynamic content:**

```http
Transfer-Encoding: chunked

3. Calculate bytes, not characters:

// ✅ Correct
const bytes = Buffer.byteLength(body, 'utf8')

// ❌ Wrong for non-ASCII text
const chars = body.length
```javascript

**4. Validate length matches actual content:**

```javascript
const body = generateResponse()
const actualLength = Buffer.byteLength(body, 'utf8')
response.setHeader('Content-Length', actualLength)

Server Examples

Express.js

app.get('/api/data', (req, res) => {
  const data = { message: 'Hello' }
  const body = JSON.stringify(data)

  res.setHeader('Content-Length', Buffer.byteLength(body))
  res.setHeader('Content-Type', 'application/json')
  res.send(body)
})
```javascript

### Python Flask

```python
from flask import Response
import json

@app.route('/api/data')
def get_data():
    data = {'message': 'Hello'}
    body = json.dumps(data)

    return Response(
        body,
        headers={'Content-Length': len(body.encode('utf-8'))}
    )

Frequently Asked Questions

What is Content-Length?

Content-Length specifies the size of the message body in bytes. It tells clients how much data to expect, enabling progress indicators and proper connection handling.

Is Content-Length required?

Not always. It is required for requests with bodies and responses without chunked encoding. With Transfer-Encoding: chunked, Content-Length is omitted.

What happens if Content-Length is wrong?

If too small, data is truncated. If too large, the connection may hang waiting for more data. Always calculate Content-Length accurately or use chunked encoding.

How do I calculate Content-Length?

Count the bytes of the body after any content encoding (like gzip). Most frameworks calculate this automatically. For streams, use chunked transfer encoding instead.

Keep Learning