HAR ツール
HARはブラウザが観測したHTTP通信をそのままJSONに落としたファイル。log.entries[]の構造、blockedからreceiveまで7つのタイミング指標、ウォーターフォールの読み方、そして共有前に消すべきCookieやトークンの扱いまでを整理します。
2 ツール
HARは通信の記録であって、パケットキャプチャではない
HAR(HTTP Archive)は、ブラウザが観測したネットワーク通信を1つのJSONに落とした
ファイルです。本体はlog.entries[]で、リクエスト/レスポンス1組が1要素になり、
メソッド・URL・ヘッダー・Cookie・サイズ・フェーズ別のタイミング、場合によっては
本文までを持ちます。仕様はHAR 1.2。W3Cのドラフトのまま標準化されませんでしたが、
Chrome・Firefox・Safari・Charles・Fiddlerが互換な方言を出力するのでツール間を
行き来できます。
押さえるべき前提は、このファイルがブラウザのネットワークスタック自身によって 事後的に書かれる点です。記録されるのはワイヤー上のバイト列ではなくスタックの 要約で、TLSハンドシェイクの記録もHTTP/2のフレームもDNSパケットも入りません。DevToolsを開く前に飛んだリクエストは残らず、Preserve logを有効に していなければ前のナビゲーションも消えます。逆にHTTPキャッシュやService Worker が返した応答は、ネットワークに出ていないのにエントリとして現れます。同じ不具合の HARが2つで食い違うのは、たいていこれが理由です。
ファイルの構造
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
request.headers[]とrequest.cookies[]は同じCookieの別表現なので、ヘッダーだけ
を消す処理ではCookieが残ります。_で始まるキー(_initiator、_priority、
_resourceType、_webSocketMessages)はベンダー拡張です。Chromeでは手掛かりに
なりますが、他の書き出し元では欠けていたり中身が違っていたりします。
数値フィールドは-1を取り得ますが、これは「値なし」であって0ではありません。
7つのタイミング指標
単位はミリ秒。発生順に並びます。
| 指標 | 測っているもの | 値が大きいとき | -1になる条件 |
|---|---|---|---|
blocked | 送出前の待ち行列 | HTTP/1.1の1オリジン約6接続の上限、プロキシ交渉 | 待たされていない |
dns | ホスト名の解決 | リゾルバのキャッシュミス、長いCNAME連鎖 | 接続を再利用 |
connect | TCPハンドシェイク(sslを含む) | オリジンが遠い、RTTが大きい、keep-aliveなし | 接続を再利用 |
ssl | TLSネゴシエーション(connectに内包) | 証明書チェーンが長い、セッション再開なし | 平文HTTPまたは再利用 |
send | リクエストの送出 | アップロード本文が大きい | — |
wait | 送信完了後のTTFB | バックエンドの処理時間+RTT 1往復 | — |
receive | 本文の受信 | 非圧縮または巨大なペイロード | — |
entry.timeは実質blocked + dns + connect + send + wait + receiveの合計です。
sslはconnectに含まれるため、7つを単純に足すとハンドシェイクを二重計上します。
これがHAR分析で最も多い計算ミスです。
ウォーターフォールの読み方
横位置はentry.startedDateTimeから所属するlog.pages[].startedDateTimeを引いた
値、バーの幅はentry.timeをフェーズで割ったものです。entry.connectionが同じ
エントリは、同一ソケットを再利用しています。
数字より形を見ます。前が終わってから次が始まる階段状の並びは依存の連鎖
(HTML → JS → API → 画像)で、サーバーを速くしても縮みません。発見を前倒しする
しかありません。下に行くほどblockedが伸びる密集帯はクライアント側の待ち行列で、
HTTP/2で個々の応答が速くならないままウォーターフォールが平らになるのはこの
ためです。
ツールの使い分け
まずHARビューアーに貼り付けます(SRE向けの一覧
にも入っています)。log.entries[]をMethod / Status / Type / Size / Time / URL
の表に展開し、N requests · X KB · Y ms totalの要約を出します。Statusセルは
4xx/5xxが赤、3xxがオレンジで色付くので、外れ値がすぐ見つかります。読むのは
メタデータだけで、本文は描画しません。
ビューアーが出すのはエントリ単位の合計値で、フェーズの内訳ではありません。内訳が
必要ならJSONPathファインダーで生のJSONを直接叩きます。
$..timingsで全件、$.log.entries[?(@.time>1000)].request.urlで遅いものだけ。
そもそもパースできない(ダウンロードが途中で切れた、貼り付けで折り返された)場合は
JSONフォーマッターにかけると、パーサーのエラー位置が分かります。
Statusの解釈はHTTPステータスコードで詰めます。番号から名称・
クラス・1行の意味を引ける表で、コードでも語句でも絞り込めるため、400と422の
切り分けはここで即座に付きます。扱うのは意味までで、リダイレクトの挙動までは
含みません(301の行は「恒久的に移動した」としか書いていません)。チェーンを読む
ときはプロトコル側の事実で補います——301と302はクライアントがPOSTをGETに
書き換えてボディを捨てることを許し、307と308は許しません。比較表は
HTTP関連ツールのリダイレクト選択表にあり、ヘッダー側も同じハブの
担当です。キャプチャがなくURLだけある場合は
HTTPヘッダーがapi.sitekits.dev経由でサーバー側
から取得し、ステータス・リダイレクト回数・全レスポンスヘッダーを返します(本文は
取得せず、URLも保存しません)。401のBearerトークンは
JWTデコーダーでローカルにデコードしてexp切れを確認でき、
再現はREST APIテスターがブラウザから対象へ直接送るので
CORSも本番と同じ挙動になります。
よくある落とし穴
生のHARは認証情報そのもの
セッションCookie、Authorization: Bearer …、x-api-key、署名付きURL、ログインの
POST本文、そのセッションで読めたレスポンス本文が全部入っています。
HARサニタイザーはブラウザ内で完結し(アップロードしません)、
該当ヘッダーとtoken|key|secret|password|passwd|pwd|auth|session|sig|signatureに一致するパラメータ、
postData.textとcontent.textを[REDACTED]に置き換え、件数を表示します。
ブラウザ側に「サニタイズ済み」の書き出しがあっても、何を消すかはバージョン次第
なので通しておきます。
パターンマッチは保証ではない
ヘッダー名は固定リスト、パラメータ名は正規表現での照合なので、変則的なものは
残ります。URLのパスに埋まった秘密(/v1/reset/9f3c…)、構造化された
cookies[]配列、serverIPAddress(オリジンの実IP)、内部ホスト名などです。出力には
必ず目を通し、怪しいURLはURLパーサーでクエリを展開して確認し、
渡す前にauthorizationとset-cookieをgrepします。同じ発想は
プライバシー関連ツールや
セキュリティ向けの一覧にも通じます。
content.sizeとbodySizeは別物
content.sizeはデコード後の本文長、bodySizeは実際に受け取ったバイト数で、
削減分はcontent.compressionに入ります。キャッシュから返った応答は
bodySize: 0です。content.sizeを足したKB合計は、圧縮されたサイトの実際の
転送量を過大に見せます。