Skip to content

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.

Dados sobre este código de status
Classe2xx, Sucesso
Definido emRFC 9110 §15.3.7
Pode ir para o cache por padrãoSim, de forma heurística; caches podem guardar o trecho e combinar faixas da mesma representação
Pode repetir o pedidoSim; peça as faixas que faltam, com If-Range para detectar arquivo alterado
Cabeçalhos relevantes
  • Content-Range: obrigatório em parte única: quais bytes vieram e o tamanho total, ex. bytes 0-1023/146515
  • Accept-Ranges: bytes anuncia que o servidor aceita pedidos com Range
  • If-Range: cabeçalho do pedido; só retoma se o ETag ou a data ainda baterem, senão vem um 200 completo

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 (Node.js)
// 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' });
});
Next.js App Router (route handler)
// 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'
    }
  });
}
Go net/http
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)
})
Python FastAPI
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)
Nginx
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;
}
Terminal
# 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.