Claude Media
Claude CodeでHARファイルを解析し失敗リクエストを絞る手順

Claude CodeでHARファイルを解析し失敗リクエストを絞る手順

顧客から届いたHARファイルを、Claude Codeで失敗リクエストの一覧に絞り込む手順です。Cookieやトークンを消す前処理と、生ファイルをClaudeに読ませない権限設定も載せています。

「画面が真っ白になる」という問い合わせに、HARファイルが添付されて届く場面があります。開けば数万行のJSONで、どこが原因か分かりません。

この作業はClaude Codeと相性が良い部類です。ただし、HARにはCookieやトークンが入っていることがあります。顧客のファイルをそのままモデルに読ませる前に、渡す中身を絞る必要があります。

この記事では、失敗リクエストを抜き出す手順と、渡す前に認証情報を消す前処理を、動かして確かめたスクリプトつきで示します。

先に決めておくこと — 生ファイルはClaudeに読ませない

結論から言うと、流れは次の順です。顧客のHARは人が前処理スクリプトにかけ、Claude Codeには消毒済みのファイルだけを渡します。

手順

HAR調査の流れ

  1. 1

    生ファイルを隔離する

    顧客から届いたHARは raw/ に置き、Claude Codeが読めないようにします。

  2. 2

    前処理スクリプトを用意する

    Claude Codeに、架空のHARを使って消毒スクリプトを書かせます。

  3. 3

    人が実行して消毒済みを作る

    生ファイルに触れるのは、人が走らせるスクリプトだけです。

  4. 4

    消毒済みだけをClaudeに渡す

    失敗リクエストの抽出と原因の絞り込みは、消毒済みファイルで行います。

スクリプトをClaude Codeに書かせるときに、実物の顧客データは要りません。ポイントはここです。形式が決まっているので、自分のブラウザで取ったダミーのHARがあれば十分です。

顧客にHARを頼むときの案内

顧客側の操作は、Chrome DevToolsのNetworkパネルからの書き出しです。ドキュメントによると、Networkパネルの上部の操作バーにあるExport HAR (sanitized)... から保存できます。リクエスト表を右クリックしてCopyの中のSave all [listed] as HAR (sanitized)を選ぶ方法もあります。

sanitizedという名の書き出しは、既定でCookie、Set-Cookie、Authorizationといったヘッダーを除きます。ドキュメントが例に挙げるのはヘッダーです。URLのクエリ文字列やリクエスト本文、レスポンス本文への言及は、Networkパネルのリファレンスにはありません。

つまりsanitizedで届いたファイルでも、?access_token=... のようなURLや、本文中のメールアドレス、パスワードが残っている可能性があります。顧客に頼む段階ではsanitizedを指定し、こちらでもう一度消す。顧客側とこちら側で2回消す前提で考えるのが安全です。

with sensitive dataの書き出しは、設定のAllow to generate HAR with sensitive dataを有効にしたときだけ選べます。サポートの案内には、この設定を有効にしないよう書いておくと、認証情報込みのファイルが届く確率を下げられます。

生ファイルをClaude Codeから遠ざける設定

raw/ を読ませない設定は、プロジェクトの .claude/settings.json にdenyルールとして書きます。

{
  "permissions": {
    "deny": ["Read(./raw/**)"]
  }
}

permissionsのドキュメントでは、評価の順序はdeny、ask、allowです。最初に一致したものが結果を決めるので、denyに入れたパスは後からallowで開けられません。Read のdenyは、Readツールのほか、cat、head、tail などのBash上のファイル読み取りコマンドにも効きます。

ただし限界があります。ドキュメントによれば、ReadとEditのdenyは、ファイル名を挙げずにファイルを読むコマンドや、PythonやNodeのスクリプトが自分で開くファイルには効きません。grep -r pattern . をそのディレクトリで走らせる例も同じ扱いです。

これは本件で大きな意味を持ちます。Claude Codeが書いたPythonスクリプトをClaude Code自身に実行させれば、raw/ の中身はdenyをすり抜けて読めてしまいます。スクリプトの実行は人が行う。この線を守ることが、denyルールの穴をふさぎます。

OSレベルで読み取りを止めたいなら、サンドボックスの sandbox.filesystem.denyRead があります。こちらはBash経由のすべてのプロセスに効きます。一方で、同じドキュメントに「denyRead の項目はReadツールを止めない」と書かれています。Readツール側は先ほどのpermissionsのdeny、Bash側はサンドボックスと、二つを併用する構成になります。

認証情報を消す前処理スクリプト

HARはJSONで、UTF-8で保存される決まりです。log.entries の各要素がリクエスト1件に当たり、request.headers、request.cookies、request.queryString、request.postData、response.content.text に値が入ります。消す対象はこのあたりです。

次のスクリプトは、サンプルのHARで動かして確かめたものです。Cookie、Authorizationなどのヘッダーと、名前にtokenやpasswordを含む項目の値を伏せます。URLのクエリ中のトークンと、本文中のメールアドレスも置き換えます。

# redact_har.py — 使い方: python3 redact_har.py raw/in.har clean/out.har
import json, re, sys
SENS_HEADERS = {"cookie", "set-cookie", "authorization",
                "proxy-authorization", "x-api-key"}
SENS_KEYS = re.compile(r"(token|secret|password|passwd|api[_-]?key|session)", re.I)
EMAIL = re.compile(r"[\w.+-]+@[\w-]+\.[\w.-]+")
 
def scrub_pairs(pairs):
    for p in pairs:
        if p["name"].lower() in SENS_HEADERS or SENS_KEYS.search(p["name"]):
            p["value"] = "[REDACTED]"
 
def scrub_text(s):
    s = EMAIL.sub("[EMAIL]", s)
    return re.sub(
        r'("[^"]*(?:token|secret|password|api[_-]?key)[^"]*"\s*:\s*)"[^"]*"',
        r'\1"[REDACTED]"', s, flags=re.I)
 
har = json.load(open(sys.argv[1], encoding="utf-8"))
for e in har["log"]["entries"]:
    req, res = e["request"], e["response"]
    for part in (req, res):
        scrub_pairs(part.get("headers", []))
        scrub_pairs(part.get("cookies", []))
    scrub_pairs(req.get("queryString", []))
    req["url"] = re.sub(
        r"([?&][^=&]*(?:token|key|secret|session)[^=&]*=)[^&]*",
        r"\1[REDACTED]", req["url"], flags=re.I)
    if "text" in req.get("postData", {}):
        req["postData"]["text"] = scrub_text(req["postData"]["text"])
    if "text" in res.get("content", {}):
        res["content"]["text"] = scrub_text(res["content"]["text"])
json.dump(har, open(sys.argv[2], "w", encoding="utf-8"),
          ensure_ascii=False, indent=1)

5件のリクエストを入れたサンプルHARで実行すると、Bearer トークン、sid のCookie、access_token=xyz789、パスワード、メールアドレスのすべてが出力から消えました。出力ファイルを grep して元の文字列が0件になることも確認しています。

この正規表現が拾えないもの

このスクリプトは名前と形に頼る検出です。拾えないものがあります。

  • 名前にtokenなどを含まないキーに入った秘密値(例: "k": "sk-...")
  • 本文中の氏名、住所、電話番号
  • 画像やbase64でエンコードされたレスポンス本文の中身
  • 社内ホスト名や内部APIのパス

消毒の後で、grep で自社固有の語や顧客名を検索してから渡す手順を足してください。個人関連情報の線引きは、Claudeにログを渡す前に確認する個人関連情報の線引きが法令の面から整理しています。HARのCookieは、そこで述べられているCookie IDの扱いと直接つながります。

失敗リクエストを抜き出す

消毒済みのファイルができたら、ようやくClaude Codeの出番です。clean/ のファイルを渡し、抽出スクリプトを書かせます。

claude -p "clean/out.har は HAR 1.2 形式です。\
status が 0 または 400 以上、あるいは timings.wait が 3000ms 超の\
リクエストを、開始時刻順に TSV で出す Python スクリプトを書いて\
triage_har.py として保存して" \
  --allowedTools "Read,Write"

標準入力にHARを流し込む方法は、大きなファイルには向きません。headlessのドキュメントでは、パイプで渡せる入力は10MBが上限で、超えるとエラーで終了します。より大きい入力はファイルに書き、プロンプトでパスを指すよう案内されています。数万行のHARは容易に超えるので、ファイルパスで渡す形が基本です。

出てきたスクリプトの核は次のとおりです。

# triage_har.py — 使い方: python3 triage_har.py clean/out.har
import json, sys
har = json.load(open(sys.argv[1], encoding="utf-8"))
rows = []
for i, e in enumerate(har["log"]["entries"]):
    st = e["response"]["status"]
    wait = e["timings"].get("wait", 0)
    if st == 0 or st >= 400 or wait > 3000:
        rows.append((e["startedDateTime"], i, e["request"]["method"], st,
                     round(e["time"]), e["request"]["url"][:90]))
for r in sorted(rows):
    print(*r, sep="\t")

同じサンプルで動かした結果は次のとおりです。

python3 triage_har.py clean/out.har
2026-10-01T10:00:01.000Z  1  POST  500  340   https://app.example.com/api/orders
2026-10-01T10:00:03.000Z  3  GET   200  5200  https://app.example.com/api/report
2026-10-01T10:00:04.000Z  4  GET   404  60    https://app.example.com/api/missing

5件中3件が絞り込まれ、500のエラー、5秒超の遅いレスポンス、404が一覧に並びました。左から開始時刻、元の entries での番号、メソッド、ステータス、所要時間(ms)、URLです。

判定の条件で見ておく点

抽出条件を決めるときは、HAR仕様の規定とChromeの表示が手がかりになります。

  • Chromeなどが書き出すHARでは、レスポンスを受け取れなかったリクエストの status が0になることがあります。DevToolsのステータス欄で (failed) と理由が出る種類のものが、これに当たりえます。0の扱いは仕様の規定ではなくブラウザ側の慣行なので、抽出条件に status == 0 を入れておくと取りこぼしを防げます
  • timings の send、wait、receive は必須で負の値を取りません。一方 blocked、dns、connect、ssl は、該当しないとき -1 が入ります。合計を自分で計算するスクリプトでは、-1 を足し込まないようにします
  • wait はサーバーからの応答待ちの時間です。サーバー側が遅いのか、回線が遅いのかは、wait と receive の大小で見分ける手がかりになります
  • 仕様では、独自の項目はアンダースコアで始まる名前にする決まりです。ブラウザ固有の _ つきの項目は環境で異なるため、抽出条件の主役にしないほうが安全です

原因を絞り込むプロンプトの型

抽出した一覧から先は、Claude Codeに判断材料を並べさせる作業になります。失敗リクエストの一覧から原因を絞るときは、次の順で頼むと根拠を追いやすくなります。

  1. 失敗リクエストをURLのパス単位でまとめ、最初に失敗した1件を特定させる
  2. その直前の成功リクエストと、同じ時刻帯の別リクエストを並べさせる
  3. レスポンス本文のエラーメッセージを読ませ、サーバー起因か、クライアント起因かを分けさせる
  4. 結論の根拠として、entries の番号を必ず添えさせる

4つ目が大事です。番号があれば、人が元のHARをDevToolsで開き直し、同じ行を確認できます。Networkパネルは、HARをドラッグ&ドロップするか、操作バーのImport HARで読み込めます。インポートしたリクエストについても、イニシエーター(どの処理が発行したか)を表示します。

CLAUDE.mdには、この調査用のルールを短く置けます。

## HAR調査のルール
- raw/ は読まない。読むのは clean/ のファイルだけ
- 結論には必ず entries の番号と URL を添える
- 推測は推測と書く。レスポンス本文にない原因を断定しない
- 顧客固有の名前を見つけたら、回答に出さず報告する

返信に使う前に確かめること

Claude Codeが出した原因の仮説は、そのまま顧客への回答にはなりません。次の3点を確認してから返信します。

確認

返信前の確認

  • 番号で元を引く

    結論に添えられた entries の番号を、実際のHARで開いて一致するか見ます。

  • 再現できるか

    可能なら同じ操作を自分の環境で行い、同じエラーが出るかを試します。HARは顧客の環境の一回きりの記録です。

  • サーバー側ログと突き合わせる

    HARの時刻とサーバーのログを照らすと、確度が上がります。ログの調べ方は、Entra IDのサインインログをKQLで調べる手順や、PLCのログをCSVから解析する手順が参考になります。

よくある質問

アプリ本体のログは同じ手順でいいか

形式が違うだけで、考え方は同じです。生ファイルを隔離し、消毒スクリプトを人が実行し、消毒済みだけを渡します。消すべき項目はHARのヘッダー名ではなく、そのログに出るIDやAuthorization相当の文字列になります。ログごとにスクリプトをClaude Codeに書かせ、サンプルで動作を確かめてから使ってください。

この記事を共有:XはてブLinkedIn