FlowingDev

HARファイル徹底解説:君のブラウザのブラックボックス・レコーダー

HAR (HTTP Archive) ファイルとは何か、ブラウザの全ネットワークリクエストをどうやって記録するのか、そしてWebパフォーマンスのデバッグになぜ不可欠なのかを解説します。

ツールを試す: HAR ビューア

一言でいうと

HARファイルとは、Webブラウザとサイト間の通信を記録したJSON形式のログファイルです。後で分析できるよう、すべてのネットワークリクエストとレスポンスを極めて詳細に記録します。

どんな問題を解決するのか

想像してみてください。地球の裏側にいるユーザーからDMが届きます。「あなたのアプリ、遅すぎて使えません」。あなたは試してみる。サクサク動く。ユーザーは壊れていると言う。あなたは「自分のマシンでは動きます」と言う。はい、膠着状態です。

これは、Web開発における「言った言わない」の典型的なパターンです。モダンなブラウザツールが登場する前は、リモート環境でのネットワーク問題のデバッグは、当てずっぽう、サーバーログの洞窟探検、そして技術に詳しくないユーザーに難解なエラーメッセージを説明してもらう、といった悪夢のような作業でした。ブラウザのDevToolsとその素晴らしいネットワークタブが登場しても、問題は残りました。データは一時的なものだったのです。それを簡単にボトルに詰めて同僚に送ることはできませんでした。ウォーターフォールチャートのスクリーンショットだけでは、全体像はわかりません。

そこで登場したのが、HTTP Archiveフォーマット、略してHARです。W3CのWeb Performance Working Groupによって考案されたこのフォーマットは、HTTPトランザクションをアーカイブするための、標準的で共有可能なフォーマットとして設計されました。これは、ユーザーのブラウザにブラックボックス・レコーダーを設置するデジタル版のようなものです。

HARファイルは、特定のWebページの読み込みにおけるブラウザとサーバー間のすべてのネットワーク通信をキャプチャすることで、「自分のマシンでは動きます」問題を解決します。画像、スクリプト、フォント、APIコールのすべてのリクエストを記録します。送信されたヘッダー、交換されたクッキー、追跡されたリダイレクト、そして最も重要なこととして、リクエストの各段階の正確なタイミングを記録します。

これにより、サンフランシスコにいる開発者が、シンガポールにいるユーザーが体験したことを、推測することなく、ミリ秒単位で正確に確認できます。再現が困難な一過性のネットワークバグを分析可能にし、「遅い」という漠然とした不満を、具体的なアクションにつながるデータに変えるのです。

内部の仕組み

核心を突くと、HARファイルは魔法ではありません。単なる、構造化されたデカいJSONファイルです。テキストエディタで開いてすべてを見ることもできますが、専用のビューアを使えば、解析が無限に簡単になります。中を覗いてみましょう。

全体構造:ただのJSON

HARファイルには、logというキーを1つだけ持つ、トップレベルのJSONオブジェクトが1つ含まれています。他のすべては、このlogオブジェクトの中に格納されています。

{
  "log": {
    "version": "1.2",
    "creator": { "name": "Chrome", "version": "118.0.0.0" },
    "browser": { "name": "Chrome", "version": "118.0.0.0" },
    "pages": [ /* ... 1つ以上のページオブジェクト ... */ ],
    "entries": [ /* ... 1つ以上のリクエスト/レスポンスオブジェクト ... */ ]
  }
}
  • version: HARの仕様バージョン。通常は "1.2" です。
  • creator / browser: どのツールとブラウザがファイルを生成したかに関するメタデータ。コンテキストを把握するのに役立ちます。
  • pages: ロードされたメインページを記述する配列です。ページのタイトルや、onLoad や onContentLoad といった高レベルなイベントのタイミングが含まれます。
  • entries: これが主役です。各オブジェクトが単一のネットワークリクエストとそれに対応するレスポンスを表す、長い配列です。

ショーの主役:entries 配列

HARファイルを分析するとき、その時間の99%を entries の中で過ごすことになります。各エントリは、1つのリソースに関する完全な調書です。

単一のエントリを簡略化して見てみましょう。

{
  "startedDateTime": "2023-10-27T10:30:05.123Z",
  "time": 258.45,
  "request": { /* ... リクエストの詳細 ... */ },
  "response": { /* ... レスポンスの詳細 ... */ },
  "timings": { /* ... パフォーマンスに関するおいしい情報が詰まった内訳 ... */ },
  "pageref": "page_1"
}
  • startedDateTime: リクエストが開始された正確なUTCタイムスタンプ。
  • time: リクエストの開始から終了までの総経過時間(ミリ秒単位)。
  • request: ブラウザがサーバーに送信したすべてを含むオブジェクト。
  • response: サーバーが返したすべてを含むオブジェクト。
  • timings: パフォーマンスデバッグの宝の山。次にこれを詳しく見ていきます。
  • pageref: このリクエストを pages 配列内のいずれかの pages にリンクするID。

リクエストとレスポンスの解剖学

request と response オブジェクトは、DevToolsで見るものの鏡です。

request オブジェクトの詳細:

  • method: GET、POST、PUTなど。
  • url: リソースの完全なURL。
  • headers: User-Agent、Accept、Cookieなど、すべてのリクエストヘッダーの配列。
  • queryString: URL上のクエリパラメータの配列。
  • postData: POSTリクエストの場合、フォームデータやJSONボディなどのペイロードを保持します。

response オブジェクトの詳細:

  • status: HTTPステータスコード(例: 200、404、500)。
  • statusText: 理由フレーズ(例: OK、Not Found)。
  • headers: Content-Type、Cache-Control、Set-Cookieなど、すべてのレスポンスヘッダーの配列。
  • content: レスポンスボディを記述するオブジェクト。size、mimeType、そして多くの場合、textプロパティにボディ自体が含まれます(ただし、スペース節約やセキュリティのために省略されることもあります)。

タイミングのウォーターフォール内訳

timings オブジェクトは、HARビューアのカラフルなウォーターフォールチャートを動かしているものです。これは、リクエストの合計 time を構成要素のフェーズに分解します。これらを理解することが、リクエストが「なぜ」遅かったのかを診断する鍵となります。

タイミング 意味
blocked リクエストが開始される前にブラウザのキューで待機していた時間。多くは接続数の上限が原因です。
dns DNSルックアップにかかった時間。この値が高い場合は、DNSプロバイダが遅い可能性があります。
connect サーバーへのTCP接続を確立するのにかかった時間。sslの時間を含みます。
ssl (connectの一部) SSL/TLSハンドシェイクの時間。この値が高いと、サーバー設定やネットワークの問題が考えられます。
send HTTPリクエストをサーバーに送信するのにかかった時間。通常は非常に短いです。
wait Time To First Byte (TTFB)。これが重要です。サーバーがリクエストを処理し、レスポンスの最初の1バイトを送信するまでの待機時間。wait時間が長いのは、ほぼ間違いなくバックエンドの問題です。
receive サーバーからレスポンスボディをダウンロードするのにかかった時間。小さいファイルでreceive時間が長い場合はネットワークの遅さが、大きいファイルの場合は当然そうなります。

エントリの合計 time は、これらの個々の(負ではない)タイミングの合計です。HARビューアがリクエストのバーを表示するとき、それはこれらの timings の値を端から端まで視覚的に積み重ねているのです。

実際の事例

謎の遅延事件

パニック状態のPMがチームにメッセージを送ってきます。「新しいチェックアウトページが、最大手のクライアントの環境ですごく遅い!契約を切ると脅されている!」開発チームがチェックアウトフローを試してみると、電光石火の速さです。クライアントは注文確定に20秒かかると主張します。不毛なやり取りの代わりに、リード開発者はクライアントにHARファイルのエクスポート方法を案内しました。

ファイルを開くと、問題は即座に明らかになりました。entries を見ると、/api/v1/finalize_order への POST リクエストの合計 time が20,145msになっています。timings オブジェクトを見ると、wait (TTFB) が20,000msを超えています。バックエンドサーバーが応答するのに20秒かかっていたのです。調べてみると、この特定のクライアントは膨大な注文履歴を持っており、最適化されていないデータベースクエリがタイムアウトしていましたが、それは彼らのアカウントでのみ発生していました。HARファイルは、特定のバックエンドプロセスを直接指し示す、決定的な証拠を提供しました。

教訓: HARファイルは、こちらでは再現できないユーザー固有の条件(アカウントデータなど)をキャプチャし、謎を的を絞ったバグレポートに変えてくれます。

肥大化したバンドルが犯人

マーケティングサイトが公開されましたが、直帰率が爆上がりしています。とにかく重い感じがするのです。フロントエンド開発者はサイトを開き、DevToolsを起動し、セッションを記録してHARをエクスポートしました。

HARビューアで、エントリをサイズ順に並べ替えます。トップには、なんと5.2 MBもの main.acb123.js があります。ウォーターフォールを見ると、これがレンダリングをブロックするリソースであることがわかります。この巨大なファイルがダウンロードを終えるまで、ページには何も表示されません。高速な接続でも receive 時間だけで数秒かかっています。さらに悪いことに、このエントリの response ヘッダーを見ると、ブラウザは request ヘッダーで Accept-Encoding: gzip を送信しているにもかかわらず、サーバーが Content-Encoding: gzip ヘッダーを送信していないことがわかりました。JavaScriptバンドルが圧縮されていなかったのです。

教訓: HARファイルを使えば、巨大すぎるアセットやサーバーの設定ミスといった、ページのロードタイムを殺しているパフォーマンスのキラーをいとも簡単に見つけ出すことができます。

無限リダイレクトループ

ユーザーがログインできないと不満を言っています。認証情報を入力して「ログイン」をクリックすると、エラーもなくすぐにログインページに蹴り戻されるとのこと。典型的なループです。サポートはユーザーに、ログイン試行時のHARファイルを依頼しました。

HARの entries リストは、明確なストーリーを語っていました:

  1. POST /login は成功し、/dashboard への 302 Redirect を受け取ります。レスポンスには、セッショントークンを含む Set-Cookie ヘッダーが含まれています。
  2. ブラウザはリダイレクトに従い、GET /dashboard リクエストを送信します。
  3. サーバーは GET /dashboard に対して、/login への 302 Redirect で応答します。

なぜでしょう?開発者は GET /dashboard リクエストのエントリを調べます。Cookie ヘッダーにセッショントークンがありません。次に、最初の POST /login からのレスポンスを確認します。Set-Cookie ヘッダーは session_id=...; Secure; HttpOnly となっていました。Secure フラグは、ブラウザがHTTPS経由でのみクッキーを送信することを意味します。ユーザーは http://staging.example.com という環境にいました。ブラウザは、安全でない接続でセキュアクッキーを送信することを正しく拒否していたため、サーバーはユーザーがログインしているとは認識していなかったのです。

教訓: HARファイルは、HTTPリダイレクトとヘッダー交換の完璧なコマ送りリプレイを提供し、サイレントに失敗する複雑な認証フローのデバッグを可能にします。

よくある間違いと落とし穴

  • 「Preserve log」を忘れる。バグがページAからページBへの移動を伴う場合、DevToolsで「Preserve log」(または同等のオプション)を有効にする必要があります。さもないと、ナビゲーション時にログがクリアされ、HARファイルにはページBのリクエストしか含まれなくなります。
  • 機密データを共有してしまう。HARファイルは無差別な記録装置です。APIキー、クッキー内のセッショントークン、POSTボディ内の個人を特定できる情報をキャプチャします。HARファイルを公開バグトラッカーやフォーラムで共有する前には、必ずサニタイズ(無害化)してください。
  • blocked時間を誤解する。blocked時間が長いからといって、必ずしもネットワークが混雑しているわけではありません。ブラウザは、単一ドメインに対して開く並列接続数に制限があります(通常は6)。一度に20個の画像リクエストを発行すると、そのうち14個は最初の6つのうちの1つが終わるのを待って blocked 状態で待機します。
  • キャッシュの状態を無視する。初回読み込みのパフォーマンスをテストしている場合は、ブラウザのキャッシュを無効にして記録する必要があります。そうしないと、304 Not Modified レスポンスや1ミリ秒未満で完了するリクエストが多く見られ、新規ユーザーの体験を反映しません。

なぜアンテナを張っておくべきなのか

ネットワーク通信が容疑者として考えられるときはいつでも、HARファイルの使用を検討すべきです。

  • ユーザーから報告されたパフォーマンス問題を再現できないとき。
  • 読み込みが遅いページを最適化する必要があり、最大のボトルネックを特定したいとき。
  • OAuthログインや支払い処理のような、複数ステップのAPIフローをデバッグしており、イベントの正確な順序を確認する必要があるとき。
  • サードパーティサービス(CDNやAPIプロバイダなど)にバグレポートを提出する必要があり、問題の反論の余地のない証拠を提供したいとき。HARファイルは、ネットワーク問題の世界共通言語です。

さらに深く

理論はOK。さあ手を動かそう — 100%ブラウザ内で。

ツールを試す: HAR ビューア