HAR(HTTP Archive)은 브라우저가 페이지를 여는 동안 주고받은 HTTP 요청과 응답을 통째로 담은 JSON 파일이다. 확장자는 .har 이지만 내용은 그냥 JSON 이라 아무 편집기로나 열 수 있다.
"우리 쪽에서는 되는데 사용자 쪽에서만 안 된다" 는 상황에서 쓴다. 사용자에게 HAR 을 받으면 어느 요청이 실패했는지, 어디서 시간을 쓰는지, 어떤 헤더가 오갔는지를 재현 없이 볼 수 있다.
브라우저마다 메뉴 이름이 조금 다르지만 절차는 같다.
Preserve log(로그 유지)를 켠다. 켜지 않으면 페이지가 이동할 때 기록이 지워진다.Save all as HAR with content(또는 내려받기 아이콘)로 저장한다.HAR 에는 쿠키, Authorization 헤더, 로그인 폼에 입력한 값, 응답 본문이 그대로 들어간다. 세션 쿠키가 살아 있는 HAR 은 그 자체로 계정 탈취 수단이다. 사용자에게 요청할 때 다음을 함께 알린다.
민감한 값을 지우고 받으려면 Save all as HAR(content 없는 형태)로 저장하게 하거나, 받은 뒤 편집기에서 해당 필드를 지운다.
가장 편한 것은 다시 개발자 도구의 Network 탭에 끌어다 놓는 것이다. 원래 화면 그대로 보인다. 별도 뷰어를 쓰기 전에 이 방법을 먼저 쓴다.
JSON 이므로 jq 로 필요한 것만 뽑을 수 있다. 요청이 수백 개일 때 훨씬 빠르다.
# 상태 코드와 URL 만
jq -r '.log.entries[] | "\(.response.status) \(.request.method) \(.request.url)"' page.har
# 실패한 요청만
jq -r '.log.entries[] | select(.response.status >= 400)
| "\(.response.status) \(.request.url)"' page.har
# 오래 걸린 순서
jq -r '.log.entries | sort_by(-.time)[] | "\(.time|floor)ms \(.request.url)"' page.har | head -20
# 특정 요청의 타이밍 분해
jq '.log.entries[] | select(.request.url|test("api/login")) | .timings' page.har
| 항목 | 읽는 법 |
|---|---|
response.status |
0 이면 연결 자체가 안 된 것이다. CORS 차단이나 인증서 오류일 때 이렇게 나온다 |
timings.blocked |
브라우저의 동시 연결 한도나 프록시 대기 |
timings.dns · timings.connect · timings.ssl |
이름 해석, TCP, TLS 핸드셰이크 각각의 시간 |
timings.wait |
요청을 보낸 뒤 첫 바이트까지. 서버 처리 시간에 가깝다 |
timings.receive |
본문을 받는 시간. 크면 응답이 크거나 대역폭 문제다 |
response.headers |
리다이렉트 대상, 캐시 헤더, CORS 헤더 |
wait 만 크면 서버 쪽, connect 나 ssl 이 크면 네트워크와 인증서 쪽, blocked 이 크면 클라이언트 쪽 대기다. 어느 구간을 파야 할지를 먼저 가르는 것이 HAR 의 쓸모다.