rea-reverse

HAR File Analyzer: How to Read and Sanitize a HAR File Before Sharing

Written for REA 6.7.0Published by rea-reverseUpdated 9 min read
On this page

A HAR file is safe to share only after you remove what it carries besides URLs: Chrome and Edge’s Export HAR (sanitized) drops Cookie, Set-Cookie and Authorization headers, but API keys in query strings, other auth headers, form posts and response bodies stay in the file. Export the sanitized version, search it for the leftovers listed below, or send a value-free endpoint summary from the HAR analyzer instead. This guide covers export in each browser, where the secrets sit, how to read a HAR by endpoint, and how to let an AI agent read individual records.

Only capture traffic you are allowed to record (your own app, a test account, or a system you are authorized to test); a HAR can contain other people’s personal data.

Read a HAR Without Sharing Its SecretsSilent walkthrough with English captions.

What a HAR file is

HAR (HTTP Archive) is a JSON format. Its top level is log, with version (normally "1.2"), creator (the browser that wrote it) and entries. Each entry is one request and response pair:

  • request: method, URL with query string, headers, cookies, and postData for a body
  • response: status, headers, cookies, and content with the response body as text (sometimes base64)
  • time and timings: how long the request took, split into phases such as wait (time to first byte)

Because the body text is usually included, a HAR is much more than a list of URLs. That is why it is useful for debugging, and why you need to be careful with it.

Step 1: Export a HAR from your browser

In every browser, open the developer tools before you reproduce the problem. The Network panel only records while it is open.

Chrome and Edge (Chromium):

  1. Open DevTools (F12) and go to Network. Tick Preserve log so a navigation does not clear the list.
  2. Reproduce the problem.
  3. Click the download icon in the Network toolbar and choose Export HAR (sanitized)….

Since Chrome 130 the default export leaves out Cookie, Set-Cookie and Authorization headers. To include them you must enable Settings > Preferences > Network > Allow to generate HAR with sensitive data. For a support ticket, the sanitized export is almost always the right choice.

Firefox: open the developer tools, go to Network, enable Persist Logs if the page navigates, reproduce the problem, then right-click the request list and choose Save All As HAR. Firefox does not apply Chrome’s sanitizing step, so treat its HAR as containing everything.

Safari: enable the Develop menu in Safari’s settings (Advanced > Show features for web developers), open the Web Inspector, go to Network, reproduce the problem and use Export to save a .har file. Treat it as unsanitized.

Keep the capture small. Start recording right before the action you care about and stop right after. A short capture is easier to read and holds less personal data.

Step 2: Know where the secrets are

Before you summarize or share a HAR, it helps to know where sensitive values hide:

Location Typical content Removed by Chrome’s sanitized export?
Authorization header Bearer tokens, Basic credentials Yes
Cookie / Set-Cookie headers and cookies arrays Session IDs Yes
Other headers such as X-Api-Key API keys No
URL query string ?token=, ?api_key=, signed URL parameters No
postData.text Passwords from a login form, personal data No
response.content.text Anything the server returned, including user records No

The last three rows are the ones people miss. A sanitized Chrome export can still contain an API key in a query string and a full user profile in a response body.

Step 3: Summarize the HAR by endpoint

A HAR with 400 entries is hard to read in a text editor. What you usually want is a list of endpoints with their counts, status codes and timings. Open the HAR analyzer and drop the file on it. The file is parsed in a Web Worker in your tab, and the worker’s Content Security Policy blocks network requests, so nothing is uploaded.

To show real output, we built a small HAR for a demo notes app at notes.example.com. It has seven entries: two note reads, one note create, a search that fails with a 500, a GraphQL query, and two scripts, one of them from a third-party host. Some requests carry a JWT bearer token and a session cookie, and the search URL carries api_key=test-key-123. This is an excerpt of the JSON export the tool produced from it:

{
  "summary": {
    "exchanges": 7,
    "endpoints": 6,
    "firstParty": "example.com",
    "thirdPartyRatio": 0.143
  },
  "endpoints": [
    {
      "host": "notes.example.com",
      "method": "GET",
      "path": "/api/notes/{id}",
      "count": 2,
      "status": { "200": 2 },
      "credentials": [
        { "where": "header", "name": "authorization", "value": "[REDACTED]",
          "length": 60, "scheme": "Bearer", "jwt": true },
        { "where": "cookie", "name": "sid", "value": "[REDACTED]", "length": 20 }
      ],
      "maxMs": 2400,
      "avgMs": 1242.5,
      "resShape": { "object": { "id": "number", "title": "string",
        "tags": { "array": "string" }, "archived": "boolean|null" } }
    },
    {
      "host": "notes.example.com",
      "method": "GET",
      "path": "/api/search",
      "status": { "500": 1 },
      "query": ["api_key", "q"],
      "credentials": [
        { "where": "query", "name": "api_key", "value": "[REDACTED]", "length": 12 }
      ],
      "maxMs": 310
    }
  ]
}

How to read it:

  • Path templates group repeated calls. /api/notes/41 and /api/notes/42 became one row, /api/notes/{id}, with count: 2. Numeric segments, UUIDs, JWTs, dates, long hex strings, e-mail addresses and token-like strings become placeholders. A slug such as order-12345 stays literal.
  • maxMs next to avgMs points at slow requests. The average of 1242.5 ms hides the real story: one of the two reads took 2400 ms. When an endpoint’s maximum is far above its average, look at that endpoint first.
  • The status map shows errors per endpoint. The search endpoint returned 500. A status of 0 means the request got no response, for example because it was blocked or cancelled.
  • resShape describes the body without the data. You see that archived is sometimes a boolean and sometimes null. That kind of difference is often the cause of a front-end bug.
  • GraphQL requests are split by operation. The demo’s /graphql request appeared as its own endpoint with "kind": "graphql" and "operation": "query Me". The query text and variables were not kept.

We also searched the full export for every secret in the demo file: the JWT, the cookie value, test-key-123, the search term and the note body text. None of them were in it. The tool never copies header, cookie, query or body values into the report. It keeps only names, types, counts and the length of the longest value.

Step 4: Decide what to share

The analyzer does not write a cleaned-up HAR. It writes a summary. That gives you three options, from safest to most complete:

  1. Share the analyzer’s JSON export. It contains no values, only hosts, path templates, names, types, counts and timings. It does contain the file name you opened. This is enough to answer “which endpoint fails and how slow is it”.
  2. Share a sanitized browser export. Use Chrome’s or Edge’s Export HAR (sanitized), then search the file yourself for API keys in query strings and for personal data in bodies (see the checklist below).
  3. Share the full HAR only when the recipient really needs bodies and headers, through a channel your organization approves. Afterwards, sign out of the test session so its tokens stop working.

Pre-share checklist:

  • Search the file for token, key, secret, password, session and eyJ (the start of most JWTs).
  • Search for your own e-mail address, name and phone number.
  • Check postData.text on login and sign-up requests.
  • Check whether the capture includes requests to unrelated tabs or sites.
  • Prefer a test account. A real account’s tokens stay valid until they expire or you log out.

What the analyzer’s credential rules cover: headers named authorization, proxy-authorization, cookie, set-cookie or x-api-key; any header or query parameter whose name contains token, secret, password, session, key, auth, signature or credential; and any header or query value that is a JWT. Those are listed by name as credentials. All other values are dropped too, because the report never stores values at all. What the analyzer cannot do is tell you whether a body field held personal data. It only shows that a field called title was a string.

Step 5: Let an AI agent read the HAR with REA

The summary tells you which request to look at. To read that request’s actual headers and body, or to let an agent compare a request with the front-end code, use inspect-web-network-capture from REA, a third-party open-source reverse-engineering CLI (setup guide). It needs no browser and never fetches the recorded URLs.

rea inspect-web-network-capture /absolute/path/notes-demo.har har --record 1 --json

--record takes zero-based entry numbers and can be repeated. Leave it out to read every entry. This is part of what REA returned for entry 1 of the demo file:

"records": [
  {
    "ordinal": 1,
    "location": { "kind": "json-pointer", "pointer": "/log/entries/1" },
    "reported": {
      "request": {
        "method": "GET",
        "url": "https://notes.example.com/api/notes/41",
        "headers": [
          { "name": "authorization", "value": null },
          { "name": "cookie", "value": null }
        ]
      },
      "response": {
        "status": 200,
        "content": {
          "mimeType": "application/json",
          "text": "{\"id\":41,\"title\":\"a\",\"tags\":[\"x\"],\"archived\":false}"
        }
      }
    },
    "redactions": [
      { "pointer": "/request/headers/0/value", "reason": "transport-credential" },
      { "pointer": "/request/headers/1/value", "reason": "transport-credential" }
    ]
  }
]

This is a different kind of tool from the analyzer. REA keeps the record as the browser wrote it, including the response body, and records an Evidence ID and the file’s SHA-256 so an agent can cite exactly which file and entry a fact came from. It removes only known transport credentials: the values of Authorization, Proxy-Authorization, Cookie and Set-Cookie headers, cookie values, and user names and passwords embedded in URLs.

Everything else stays. When we read entry 4, the failing search, the URL came back unchanged as https://notes.example.com/api/search?q=invoice&api_key=test-key-123, with an empty redactions list. To exclude a value like that, declare it:

rea inspect-web-network-capture /absolute/path/notes-demo.har har \
  --record 4 --sensitive-value 'test-key-123' --json

With that flag, REA replaced the whole url field with null and recorded {"pointer": "/request/url", "reason": "explicit-sensitive-value"}. The key did not appear anywhere in the output, and the Evidence parameters store only "sensitive_value_count": 1, not the value. Note that the entire field is dropped, not just the matching part, so you lose the rest of the URL for that record.

Over MCP, an agent calls inspect_web_network_capture with capture_path, format: "har", and optionally record_ordinals and sensitive_values. A good prompt:

Use REA to read /absolute/path/notes-demo.har. Find the requests that failed or
took more than one second, show me their request and response, and cite the
record ordinal for each. Treat test-key-123 as a sensitive value.

REA can also group a HAR into endpoints with the api-surface view. On the CLI each command is a separate process, so hand the view the full Evidence inline:

rea inspect-web-network-capture /absolute/path/notes-demo.har har --json > har-evidence.json
jq '{source: {kind: "inline", evidence: .}, view: {kind: "api-surface", offset: 0, limit: 50}}' \
  har-evidence.json > api-surface-request.json
rea inspect-analysis-view /absolute/path/api-surface-request.json --json

Compared with the analyzer above on the same demo file, it merged /api/notes/41 and /42 into /api/notes/{numeric} and listed the other five requests as single-record groups, but it has no timings, credential list or response shapes, and /graphql stays one POST group. What it adds is record ordinals you can pass straight back to --record. It keeps no query values (test-key-123 was absent) but does keep the path segments it templated ("41", "42"), so treat ID-bearing paths as sensitive. Over MCP, request detail: "summary" first and pass the returned parent_evidence_id as a retained-evidence source to the view: in our run the reply shrank from 16,948 to 4,072 bytes. On the CLI that reference fails with evidence_integrity_mismatch, because each CLI process exits, so pass the Evidence inline.

The same command reads Fiddler .saz archives (... notes-demo.saz saz --record 1 --json) with the same credential nulling and --sensitive-value handling. We tested it on a SAZ we wrote ourselves in Fiddler’s raw/NN_c.txt / NN_s.txt / NN_m.xml layout. The record shape differs from HAR: ordinals start at 0 while Fiddler’s session numbers start at 01, the status is a string ("200"), header values keep the space after the colon, bodies are raw bytes (not dechunked or decompressed), and api-surface rejects SAZ Evidence with unsupported_target.

Troubleshooting

The analyzer says the JSON is not a HAR 1.2 log. The file lacks log.entries, or an entry has no request method, URL or response status. Export it again from the browser rather than editing it by hand.

No credentials show up for an endpoint you know is authenticated. Your HAR probably came from Chrome’s sanitized export, which removed Authorization and cookies before the analyzer ever saw them. That is fine for sharing. For an audit of which endpoints send credentials, use the export with sensitive data, analyze it locally and delete it afterwards.

REA rejects the file. For HAR, REA wants version 1.2 and rejects duplicate JSON keys. For SAZ, the archive must be unencrypted. Both formats stop at 32 MiB. Export a shorter capture that covers just the action you are debugging.

The same endpoint appears twice. The path has a segment the placeholder rules treat as a word, for example order-12345. The rows are still correct. They are just not merged.

FAQ

Can I open a HAR without any tool?

Yes, it is JSON. jq '.log.entries[] | [.request.method, .request.url, .response.status, .time]' file.har gives a quick table. You will still want path templates and redaction before sharing the result.

What is the difference between this and tracing API calls to code?

This guide is about the HAR file itself. To go from a request to the JavaScript that sent it, start from the Initiator column of your browser’s DevTools Network panel, which shows the script and call stack behind each request.