har

HAR ferramentas

Um arquivo HAR é uma transcrição em JSON de tudo o que o seu navegador viu na rede: como ele é estruturado, o que significam suas sete fases de tempo e o que ocultar antes de compartilhá-lo.

2 ferramentas

§01 GUIA DO TEMA

Um HAR é uma transcrição, não uma captura de pacotes

Um arquivo HAR (HTTP Archive) é um único documento JSON que descreve o que um navegador observou na rede. Tudo o que interessa mora em log.entries[]: um elemento por par requisição/resposta, carregando método, URL, cabeçalhos, cookies, tamanhos, um detalhamento de tempos fase por fase e — às vezes — os corpos. O formato é o HAR 1.2, um rascunho do W3C que nunca foi ratificado e, mesmo assim, Chrome, Firefox, Safari, Charles e Fiddler emitem dialetos parecidos o bastante para que os arquivos circulem entre eles.

O modelo mental que economiza horas: o arquivo é escrito pela própria pilha de rede do navegador, depois do fato — ele guarda o resumo dessa pilha, não o que passou no cabo. Nenhum registro de handshake TLS, nenhum frame HTTP/2, nenhum pacote DNS, apenas as durações que a pilha decidiu relatar. As requisições que dispararam antes de o DevTools ser aberto não estão ali, assim como a navegação anterior, a menos que Preserve log estivesse ativo, e acertos de cache ou de service worker aparecem como entradas que nunca tocaram a rede. Duas capturas do «mesmo» bug discordam rotineiramente.

Anatomia do arquivo

log
├─ version "1.2" · creator{name,version} · browser
├─ pages[]     id, title, startedDateTime, pageTimings{onContentLoad,onLoad}
└─ entries[]   pageref, startedDateTime, time, connection, serverIPAddress
   ├─ request  method, url, httpVersion, headers[], cookies[],
   │           queryString[], postData{mimeType,text,params}
   ├─ response status, statusText, redirectURL, headers[], cookies[],
   │           content{size,compression,mimeType,text,encoding}
   ├─ cache    beforeRequest, afterRequest
   └─ timings  blocked, dns, connect, ssl, send, wait, receive

Dois detalhes pegam as pessoas. request.headers[] e request.cookies[] são representações separadas dos mesmos cookies, então uma limpeza que só percorre os cabeçalhos deixa o pote intacto. Chaves que começam com _ (_initiator, _priority, _resourceType, _webSocketMessages) são extensões proprietárias: úteis no Chrome, ausentes em outros lugares. E praticamente qualquer campo numérico pode valer -1não disponível, não zero — incluindo bodySize, pageTimings e cada fase de tempo.

As sete fases de tempo

Em milissegundos, na ordem em que ocorrem.

FaseMedeUm valor alto normalmente significa-1 quando
blockedEspera na fila antes de a requisição sair do clienteO teto de cerca de 6 por origem do HTTP/1.1; negociação de proxyNunca ficou na fila
dnsResolução do nome do hostResolvedor frio, cadeia longa de CNAMEConexão reaproveitada
connectHandshake TCP — inclui sslOrigem distante, RTT alto, sem keep-aliveConexão reaproveitada
sslNegociação TLS (contada dentro de connect)Cadeia de certificados grande, sem retomada de sessãoHTTP puro, ou reaproveitamento
sendEmpurrar os bytes da requisição para foraUm corpo de upload grande
waitTTFB depois do último byte da requisiçãoProcessamento do backend mais um RTT
receiveLer o corpo vindo da redePayload sem compressão ou superdimensionado

entry.time é o tempo total decorrido, na prática blocked + dns + connect + send + wait + receive. Como ssl está aninhado dentro de connect, somar todas as sete colunas conta o handshake duas vezes — o erro aritmético mais comum na análise de HAR.

Reconstruir o waterfall

Os dois eixos podem ser recuperados do JSON: a posição horizontal é entry.startedDateTime menos o log.pages[].startedDateTime da página à qual a entrada pertence; a largura é entry.time, dividida nas fases acima. entry.connection mostra quais requisições reaproveitaram um socket.

Depois leia a forma, não os números. Uma escada — cada requisição começando quando a anterior termina — é uma cadeia de dependências, HTML → JS → API → imagem, e nenhum ajuste de servidor resolve isso; o que precisa ser antecipado é a descoberta. Um bloco denso cujo segmento blocked cresce conforme você desce na lista é enfileiramento no cliente, e é por isso que o HTTP/2 frequentemente achata um waterfall sem que nenhuma resposta fique mais rápida.

Escolher a ferramenta certa

Cole a captura primeiro no Visualizador HAR — que também está na seleção para SRE. Ele achata log.entries[] em Method / Status / Type / Size / Time / URL com um resumo N requests · X KB · Y ms total, colorindo a célula Status de vermelho para 4xx/5xx e de laranja para 3xx: o jeito mais rápido de achar o ponto fora da curva. Só metadados, nunca os corpos.

O visualizador dá totais por entrada, não a divisão por fases. Para as fases, consulte o arquivo bruto com o Buscador JSONPath$..timings para todos os detalhamentos, ou $.log.entries[?(@.time>1000)].request.url só para as lentas. Se a exportação não for interpretada (download truncado, colagem com quebras de linha), passe-a pelo Formatador JSON para obter a posição exata do erro segundo o parser.

Para a coluna Status, os Códigos HTTP transformam um número solto em nome, classe e um significado de uma linha, filtráveis por código ou palavra-chave — a forma rápida de resolver 400 contra 422. Eles param no significado, não no comportamento de redirecionamento: a linha 301 diz apenas que o recurso foi movido permanentemente. Ao ler uma cadeia, acrescente isto que vem do próprio protocolo — 301 e 302 permitem que um cliente reescreva POST como GET e descarte o corpo, enquanto 307 e 308 não. A comparação completa é a matriz de redirecionamentos no conjunto de ferramentas HTTP, que também cobre o lado dos cabeçalhos. Sem captura, apenas com uma URL, os Cabeçalhos HTTP a buscam no servidor a partir de api.sitekits.dev e devolvem status, contagem de saltos de redirecionamento e todos os cabeçalhos de resposta — o corpo nunca é recuperado, a URL nunca é armazenada. Decodifique o token bearer de um 401 com o Decodificador JWT para ver se exp já havia passado; repita-o com o Testador REST API, que envia do seu navegador direto ao destino, então o CORS se aplica como no seu aplicativo.

Pegadinhas

Um HAR bruto é uma credencial

Cookies de sessão, Authorization: Bearer …, x-api-key, URLs assinadas, corpos de login e cada resposta que a sua sessão conseguia ler estão ali dentro. O Sanitizador HAR roda inteiramente no seu navegador — nada é enviado — substituindo cabeçalhos sensíveis, parâmetros que casam com token|key|secret|password|passwd|pwd|auth|session|sig|signature e valores inteiros de postData.text / content.text por [REDACTED], e informando quantos ele pegou. Faça isso mesmo que o seu navegador tenha oferecido uma exportação «sanitizada» — o que essas removem varia conforme a versão.

Ocultar por padrões não é prova

Os nomes de cabeçalho são comparados com uma lista fixa e os nomes de parâmetro com uma expressão regular, então tudo o que é fora do convencional sobrevive: segredos em segmentos de caminho de URL (/v1/reset/9f3c…), os arranjos estruturados cookies[], serverIPAddress (o IP real da sua origem), nomes de host internos. Passe os olhos pela saída, cole qualquer URL suspeita no Parseador de URL para ver a query string decodificada e procure por authorization e set-cookie antes de o arquivo sair da sua máquina. O mesmo reflexo vale nas ferramentas de privacidade e na seleção para segurança.

content.size e bodySize medem coisas diferentes

content.size é o comprimento do corpo decodificado; bodySize são os bytes efetivamente recebidos, com content.compression registrando a economia. Respostas servidas do cache informam bodySize: 0, então um total em KB calculado a partir de content.size exagera o custo de rede de um site comprimido.

FAQ
Por que o tempo total do meu HAR não coincide com o tempo de carregamento que eu medi?
Porque as requisições se sobrepõem. Somar entry.time ao longo de log.entries[] conta duas vezes cada milissegundo em que duas requisições estavam em voo, então a soma costuma ser várias vezes o tempo de relógio. Para o tempo real, leia log.pages[].pageTimings.onLoad, ou tome o intervalo entre o startedDateTime mais antigo e o startedDateTime mais recente somado ao time dele.
O que uma fase wait longa em uma entrada HAR significa de fato?
wait é o tempo até o primeiro byte, medido depois que o último byte da requisição foi enviado, então cobre o processamento do próprio servidor mais uma ida e volta pela rede. Um wait gordo com um receive magro aponta para o backend: uma consulta lenta, um cache frio, um salto entre regiões. A forma oposta, wait magro e receive gordo, significa que a resposta era simplesmente grande ou que o enlace estava lento.
Eu ocultei os cabeçalhos Cookie do meu HAR — por que os cookies de sessão continuam no arquivo?
Porque o HAR guarda os cookies duas vezes. request.headers[] contém a linha Cookie bruta, enquanto request.cookies[] e response.cookies[] contêm os mesmos valores já interpretados como objetos nome/valor, então qualquer passada que só percorra os cabeçalhos deixa o pote intacto — inclusive o Sanitizador HAR deste site, que oculta valores de cabeçalho, parâmetros de consulta e de POST e ambos os corpos, mas não toca nos arranjos cookies[]. Procure «cookies» no arquivo antes de anexá-lo e, já que está nisso, confira serverIPAddress e qualquer segredo alojado em um segmento de caminho de URL.
Por que algumas entradas HAR mostram dns -1 e connect -1?
Aquelas fases não aconteceram naquela requisição. No HAR 1.2, -1 significa que o valor não se aplica ou não está disponível, que é o que você obtém quando a requisição viajou por uma conexão keep-alive já existente e portanto não precisou de resolução DNS, nem de handshake TCP, nem de negociação TLS. Tratar -1 como zero é inofensivo; calcular a média dele como se fosse uma medição real não é.
Por que os corpos de requisição e resposta estão ausentes do meu HAR?
postData.text e content.text são campos opcionais, e o DevTools rotineiramente omite payloads grandes ou binários para manter a exportação gerenciável. Quando há um corpo, ele pode vir em Base64 em vez de texto puro, o que é sinalizado por content.encoding com valor base64. Um corpo ausente significa que o exportador não o registrou, não que a resposta estava vazia.