HAR herramientas
Un archivo HAR es una transcripción JSON de todo lo que tu navegador vio en la red: cómo está estructurado, qué significan sus siete fases de tiempo y qué hay que censurar antes de compartirlo.
2 herramientas
Un HAR es una transcripción, no una captura de paquetes
Un archivo HAR (HTTP Archive) es un único documento JSON que describe lo que un
navegador observó en la red. Todo lo interesante vive en log.entries[]: un
elemento por cada par petición/respuesta, con el método, la URL, los encabezados,
las cookies, los tamaños, un desglose de tiempos fase por fase y — a veces — los
cuerpos. El formato es HAR 1.2, un borrador del W3C que nunca se ratificó, y aun
así Chrome, Firefox, Safari, Charles y Fiddler emiten dialectos lo bastante
parecidos para que los archivos circulen entre ellos.
El modelo mental que te ahorra horas: el archivo lo escribe la propia pila de red del navegador, a posteriori; contiene el resumen de esa pila, no el cable. Ningún registro de negociación TLS, ninguna trama HTTP/2, ningún paquete DNS, solo las duraciones que la pila decidió informar. Las peticiones que salieron antes de abrir DevTools no están, como tampoco la navegación anterior salvo que Preserve log estuviera activo, y los aciertos de caché o de service worker aparecen como entradas que nunca tocaron la red. Dos capturas del «mismo» error casi nunca coinciden.
Anatomía del archivo
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
Dos detalles pillan a la gente. request.headers[] y request.cookies[] son
representaciones separadas de las mismas cookies, así que una limpieza que solo
recorra los encabezados deja el tarro intacto. Las claves que empiezan por _
(_initiator, _priority, _resourceType, _webSocketMessages) son extensiones
propias del fabricante: útiles en Chrome, ausentes en el resto. Y casi cualquier
campo numérico puede valer -1 — no disponible, no cero — incluidos
bodySize, pageTimings y cada fase de tiempo.
Las siete fases de tiempo
En milisegundos, en el orden en que ocurren.
| Fase | Mide | Un valor alto suele significar | -1 cuando |
|---|---|---|---|
blocked | Espera en cola antes de que la petición salga del cliente | El tope de unas 6 por origen de HTTP/1.1; negociación de proxy | Nunca estuvo en cola |
dns | Resolución del nombre del host | Resolutor frío, cadena larga de CNAME | Conexión reutilizada |
connect | Negociación TCP — incluye ssl | Origen lejano, RTT alto, sin keep-alive | Conexión reutilizada |
ssl | Negociación TLS (contada dentro de connect) | Cadena de certificados larga, sin reanudación de sesión | HTTP sin cifrar, o reutilización |
send | Envío de los bytes de la petición | Un cuerpo de subida grande | — |
wait | TTFB tras el último byte de la petición | Procesamiento del backend más un RTT | — |
receive | Lectura del cuerpo desde la red | Carga útil sin comprimir o sobredimensionada | — |
entry.time es el tiempo total transcurrido, en la práctica
blocked + dns + connect + send + wait + receive. Como ssl está anidado dentro
de connect, sumar las siete columnas cuenta dos veces la negociación: el error
aritmético más habitual al analizar un HAR.
Reconstruir la cascada
Ambos ejes se pueden recuperar del JSON: la posición horizontal es
entry.startedDateTime menos el log.pages[].startedDateTime al que pertenece; el
ancho es entry.time, dividido en las fases de arriba. entry.connection muestra
qué peticiones reutilizaron un socket.
Después lee la forma, no los números. Una escalera — cada petición empezando
cuando termina la anterior — es una cadena de dependencias, HTML → JS → API →
imagen, y ningún ajuste del servidor la arregla; lo que tiene que adelantarse es el
descubrimiento. Un bloque denso cuyo segmento blocked crece a medida que bajas
por la lista es cola en el cliente, y por eso HTTP/2 aplana a menudo una cascada
sin que ninguna respuesta sea más rápida.
Elegir la herramienta adecuada
Pega primero la captura en el Visor HAR, que además está en la
selección para SRE. Aplana log.entries[] en
Method / Status / Type / Size / Time / URL con un resumen
N requests · X KB · Y ms total, y colorea la celda Status en rojo para los
4xx/5xx y en naranja para los 3xx: la forma más rápida de encontrar el caso
raro. Solo metadatos, nunca los cuerpos.
El visor da totales por entrada, no el desglose por fases. Para las fases,
consulta el archivo en bruto con el Buscador JSONPath:
$..timings para todos los desgloses, o
$.log.entries[?(@.time>1000)].request.url para solo las lentas. Si la
exportación no se analiza (descarga truncada, pegado con saltos de línea), pásala
por el Formateador JSON para obtener la posición exacta del
error según el analizador.
Para la columna Status, los Códigos HTTP convierten un número
suelto en nombre, clase y significado de una línea, filtrables por código o
palabra clave: la manera rápida de zanjar 400 frente a 422. Se detienen en el
significado, no en el comportamiento de redirección: su fila 301 solo dice que el
recurso se movió de forma permanente. Para leer una cadena, añade esto que viene
del propio protocolo: 301 y 302 permiten que un cliente reescriba un POST
como GET y descarte el cuerpo, mientras que 307 y 308 no. La comparación
completa es la matriz de redirecciones de las
herramientas HTTP, que cubre también el lado de los encabezados.
Sin captura, con solo una URL, las Cabeceras HTTP la solicitan
desde el servidor a api.sitekits.dev y devuelven el estado, el número de saltos
de redirección y todos los encabezados de respuesta — el cuerpo nunca se recupera y
la URL nunca se almacena. Descodifica el token portador de un 401 con el
Decodificador JWT para ver si exp ya había pasado; repítelo
con el Probador REST API, que envía desde tu navegador
directo al destino, así que el CORS se aplica igual que en tu aplicación.
Trampas
Un HAR en bruto es una credencial
Ahí dentro están las cookies de sesión, Authorization: Bearer …, x-api-key,
las URL firmadas, los cuerpos de inicio de sesión y todas las respuestas que tu
sesión pudiera leer. El Sanitizador HAR se ejecuta por
completo en tu navegador — nada se sube — y sustituye los encabezados sensibles,
los parámetros que coinciden con
token|key|secret|password|passwd|pwd|auth|session|sig|signature y los valores
completos de postData.text / content.text por [REDACTED], informando de
cuántos ha detectado. Hazlo incluso si tu navegador te ofreció una exportación
«saneada»: lo que esas eliminan varía según la versión.
La censura por patrones no es una garantía
Los nombres de encabezado se comparan con una lista fija y los nombres de
parámetro con una expresión regular, así que sobrevive todo lo poco convencional:
secretos en los segmentos de ruta de una URL (/v1/reset/9f3c…), los arreglos
estructurados cookies[], serverIPAddress (la IP real de tu origen), nombres de
host internos. Repasa la salida, pega cualquier URL sospechosa en el
Parseador de URL para ver la cadena de consulta descodificada, y
busca authorization y set-cookie antes de que el archivo salga de tu equipo. El
mismo reflejo vale para las herramientas de privacidad y la
selección para seguridad.
content.size y bodySize miden cosas distintas
content.size es la longitud del cuerpo descodificado; bodySize son los bytes
realmente recibidos, y content.compression registra el ahorro. Las respuestas
servidas desde la caché informan bodySize: 0, así que un total en KB calculado a
partir de content.size exagera el coste de red de un sitio comprimido.