一言でいうと
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 リストは、明確なストーリーを語っていました:
POST /loginは成功し、/dashboardへの302 Redirectを受け取ります。レスポンスには、セッショントークンを含むSet-Cookieヘッダーが含まれています。- ブラウザはリダイレクトに従い、
GET /dashboardリクエストを送信します。 - サーバーは
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ファイルは、ネットワーク問題の世界共通言語です。
さらに深く
- HAR 1.2 Specification: HARファイルの構造を定義する、オリジナルのデファクトスタンダード仕様。
- Google Chrome DevTools: Network features reference: HARファイルを生成するために最も一般的に使用されるツールに関する詳細なガイド。
- MDN Web Docs: ネットワークリクエスト一覧: FirefoxのDevToolsでネットワークリクエストを解釈するためのMozillaによる優れたドキュメント。
- What is a HAR File?: トラブルシューティングのためにHARファイルを生成・使用する方法についての、優れた高レベルの概説。