- Home
- HTTP Headers
- Content-Length
Header
Content-Length
Learn how Content-Length specifies the body size in bytes. Essential for progress indicators, connection management, and chunked transfer decisions.
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'))}
)
Related Headers
- Transfer-Encoding - Alternative to Content-Length for dynamic content
- Content-Type - Format of the content
- Content-Encoding - Compression applied to content
- Content-Range - Partial content ranges
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.