Código de status HTTP · Sucesso (2xx)
206 Partial Content
O 206 Partial Content significa que o servidor está devolvendo só a faixa de bytes que o cliente pediu no cabeçalho Range, não o arquivo inteiro. Ele aparece o tempo todo quando o navegador toca vídeo ou áudio e pula para outro trecho, e quando um gerenciador de downloads retoma um download interrompido.
| Classe | 2xx, Sucesso |
|---|---|
| Definido em | RFC 9110 §15.3.7 |
| Pode ir para o cache por padrão | Sim, de forma heurística; caches podem guardar o trecho e combinar faixas da mesma representação |
| Pode repetir o pedido | Sim; peça as faixas que faltam, com If-Range para detectar arquivo alterado |
| Cabeçalhos relevantes |
|
O que significa o 206
O cliente envia Range: bytes=21010-47021 e o servidor responde 206 com Content-Range: bytes 21010-47021/47022, em que o número depois da barra é o tamanho total. A RFC 9110, seção 15.3.7, exige o Content-Range numa parte única e diz que o Content-Length passa a contar só os bytes daquela mensagem. Várias faixas num mesmo pedido voltam num corpo multipart/byteranges.
Retomar com segurança depende do If-Range. O cliente envia o ETag ou o Last-Modified que viu antes; se o arquivo mudou desde então, o servidor ignora o Range e manda o arquivo novo inteiro com 200, e o cliente nunca junta pedaços de duas versões. Um servidor que não consegue atender a faixa responde 416.
O 206 pode ser armazenado em cache de forma heurística, e caches podem combinar faixas guardadas da mesma representação. O servidor anuncia suporte com Accept-Ranges: bytes; um servidor que ignora o Range simplesmente responde 200 com tudo, o que é válido, mas impede pular trechos e retomar downloads.
Quando usar
- Servir vídeo, áudio e downloads grandes; deixe a camada de arquivos estáticos cuidar disso em vez de interpretar o Range na mão.
- Servir arquivos de um object storage pela sua aplicação: repasse o cabeçalho Range para a API de armazenamento e devolva o 206 e o Content-Range dela.
Causas comuns
Se você está visitando o site
- Um download retomado recomeça do zero: o servidor não aceita faixas, ou o arquivo mudou e o If-Range forçou um 200 completo.
Se você administra o servidor
- O vídeo não toca nem avança no Safari: o Safari testa com Range: bytes=0-1 e desiste quando o servidor responde 200 com o arquivo inteiro.
- Pular trechos quebra atrás de um proxy ou rota da aplicação que transmite arquivos mas descarta o cabeçalho Range ou troca o status para 200.
- O Content-Range e o tamanho real do corpo não batem (muitas vezes depois de compressão na hora), e o player trava ou corrompe o quadro.
Como resolver
Se você está visitando o site
- Use um gerenciador de downloads ou navegador que retome downloads, e recomece se o arquivo foi atualizado no servidor.
Se você administra o servidor
- Sirva mídia com express.static, http.ServeContent, FileResponse do Starlette ou nginx, que já implementam Range, If-Range e 416.
- Teste com curl -s -D - -o /dev/null -H "Range: bytes=0-99" URL e espere 206 com Content-Range: bytes 0-99/total.
- Não comprima vídeo ou áudio na hora; eles já são comprimidos e as faixas precisam se referir aos bytes guardados.
Como enviar um 206
// express.static and res.sendFile answer Range requests
// with 206 Partial Content (and 416 when the range is invalid)
app.use('/media', express.static('media'));
app.get('/downloads/:name', (req, res) => {
res.sendFile(req.params.name, { root: 'downloads' });
});// app/media/[name]/route.ts (single range only)
import { open, stat } from 'node:fs/promises';
export async function GET(request: Request) {
const path = 'media/intro.mp4';
const { size } = await stat(path);
const m = /^bytes=(\d+)-(\d*)$/.exec(request.headers.get('range') ?? '');
if (!m) return new Response(null, { status: 200, headers: { 'Accept-Ranges': 'bytes' } }); // stream the full file here
const start = Number(m[1]);
const end = m[2] ? Math.min(Number(m[2]), size - 1) : size - 1;
if (start > end) {
return new Response(null, { status: 416, headers: { 'Content-Range': `bytes */${size}` } });
}
const file = await open(path);
const chunk = Buffer.alloc(end - start + 1);
await file.read(chunk, 0, chunk.length, start);
await file.close();
return new Response(chunk, {
status: 206,
headers: {
'Content-Range': `bytes ${start}-${end}/${size}`,
'Accept-Ranges': 'bytes',
'Content-Type': 'video/mp4'
}
});
}mux.HandleFunc("GET /media/{name}", func(w http.ResponseWriter, r *http.Request) {
f, err := os.Open(filepath.Join("media", filepath.Base(r.PathValue("name"))))
if err != nil {
http.NotFound(w, r)
return
}
defer f.Close()
info, _ := f.Stat()
// Handles Range and If-Range: 206 (StatusPartialContent), 416 or 200
http.ServeContent(w, r, info.Name(), info.ModTime(), f)
})from pathlib import Path
from fastapi import FastAPI
from fastapi.responses import FileResponse
app = FastAPI()
@app.get("/media/{name}")
def get_media(name: str):
# Current Starlette answers Range requests on FileResponse with 206
return FileResponse(Path("media") / Path(name).name)location /media/ {
root /srv;
# Static files get Range support (206) out of the box.
# Multi-range requests are then answered with the full file:
max_ranges 1;
}# Ask for the first 100 bytes and print only the headers
curl -s -D - -o /dev/null -H "Range: bytes=0-99" https://example.com/media/intro.mp4
# Expect: HTTP/2 206 and content-range: bytes 0-99/<total>Costuma ser confundido com
- 206 vs 200
- Um 200 para um pedido com Range indica que o servidor ignorou a faixa e mandou o arquivo inteiro, o que é permitido mas quebra busca e retomada.
- 206 vs 416
- O 416 Range Not Satisfiable indica que nenhuma parte da faixa pedida existe, geralmente porque ela começa depois do fim do arquivo.
Perguntas frequentes
- Por que pedidos de vídeo aparecem como 206 no DevTools?
- Os elementos de mídia baixam o vídeo em faixas para começar a tocar rápido e pular para qualquer ponto. Cada linha 206 é um pedaço do mesmo arquivo, e isso é normal.
- Como saber se um servidor aceita requisições Range?
- Rode curl -I e procure Accept-Ranges: bytes, depois peça uma faixa pequena com -H "Range: bytes=0-99". Um 206 com Content-Range confirma o suporte; um 200 com o tamanho inteiro indica que as faixas são ignoradas.
- O que acontece se o arquivo mudar com o download pausado?
- Se o cliente retoma com If-Range levando o ETag ou a data antiga, o servidor percebe a diferença e manda o arquivo novo inteiro com 200 em vez de 206, e você nunca recebe uma mistura de duas versões.
- Uma resposta 206 pode ir para o cache?
- Pode. A RFC 9110 torna o 206 armazenável de forma heurística, salvo controles de cache explícitos, e um cache pode combinar faixas guardadas da mesma representação.
Revisado em por Arielton Oberek.