What a HAR file leaks — and how to scrub it before you share it
published
TL;DR
A HAR file is a verbatim recording of your network traffic, not a screenshot of it. Every Authorization header, Cookie, Set-Cookie, CSRF token, and request body that crossed the wire while you were recording is sitting in plaintext JSON inside it. Sending an unredacted HAR to a vendor’s support desk hands them a working session. Strip the sensitive fields first.
The problem
Support tells you: “Open DevTools, go to the Network tab, reproduce the bug, then right-click → Save all as HAR and send it over.” You do. The bug gets fixed.
What you also did was email a file containing your live session cookie, your Authorization: Bearer … token, and the JSON body of every form you submitted — including the one with your password in it. A HAR is a .har file, but it is just JSON with a documented schema (HAR 1.2). Anyone who opens it sees exactly what the browser sent and received.
What is actually inside
The structure is log.entries[], one entry per request. Each entry has a request and a response, and the secrets live in predictable places:
| Field | What it holds | Why it is dangerous |
|---|---|---|
request.headers[] | Authorization, Cookie, X-Api-Key, Proxy-Authorization | Bearer tokens and session cookies are valid until they expire — often hours or days |
request.cookies[] | Parsed cookie name/value pairs | Same session, separately parsed for convenience |
request.queryString[] | URL query params | API keys passed in the URL (?api_key=…) land here |
request.postData.text | Raw request body | Login forms, JSON payloads, PII, passwords, card numbers |
response.headers[] | Set-Cookie | Freshly issued session cookies |
response.content.text | Raw response body | API responses with tokens, email addresses, internal IDs |
A single login flow captured in a HAR typically contains the password (in request.postData.text), the issued session cookie (in response.headers Set-Cookie), and that same cookie replayed on every subsequent request.
Why it happens
DevTools records at the HTTP layer, below TLS. TLS protects the bytes in transit; once your browser has decrypted a response to render it, the plaintext is available to the Network panel, and the HAR export serializes that plaintext. There is no “secrets” layer the browser knows to omit — Authorization is just another header to it. The format was designed for performance analysis (waterfall timings live in entry.timings), and the full request/response capture is a side effect that nobody scrubs by default.
Chrome added a “redact sensitive data” checkbox to its HAR export in 2024, but it only masks a known list of auth-style fields and does not touch postData or response bodies. Treat it as a partial measure, not a guarantee.
What to do
Inspect first, then redact. The safest inspection is local: the HAR viewer on this site parses the file entirely in your browser — nothing is uploaded — so you can see which entries carry credentials before you decide what to share.
To redact programmatically, the HAR is just JSON. This Node script blanks the common offenders and drops all bodies:
import { readFileSync, writeFileSync } from 'node:fs';
const SENSITIVE = new Set([
'authorization',
'cookie',
'set-cookie',
'x-api-key',
'proxy-authorization',
]);
const redactHeaders = (headers = []) =>
headers.map((h) =>
SENSITIVE.has(h.name.toLowerCase()) ? { ...h, value: '[REDACTED]' } : h,
);
const har = JSON.parse(readFileSync(process.argv[2], 'utf8'));
for (const { request, response } of har.log.entries) {
request.headers = redactHeaders(request.headers);
response.headers = redactHeaders(response.headers);
request.cookies = [];
response.cookies = [];
if (request.postData) request.postData.text = '[REDACTED]';
if (response.content) delete response.content.text;
}
writeFileSync(process.argv[3] ?? 'redacted.har', JSON.stringify(har, null, 2));
Run it: node redact-har.mjs raw.har redacted.har. Adjust the SENSITIVE set and the body handling to your case — if the bug is in a response body, you may need to keep that body but strip a token out of it by hand.
Beyond the script:
- Re-record narrowly. Reproduce the bug in a fresh incognito window with only the failing flow, so the HAR contains as few requests as possible.
- Rotate after sharing. If a real token or cookie made it into a HAR you sent, assume it is compromised and invalidate the session.
- Prefer a sanitizer that knows the schema. Google’s open-source har-sanitizer runs client-side and scrubs a broader field list than the Chrome checkbox.
Caveats
- Redacting headers and cookies does not redact secrets that appear inside a body — a token returned in
response.content.text, an email in a JSON payload. Those need targeted edits. - A redacted HAR may no longer reproduce the bug if the issue depends on the exact header that was stripped. Coordinate with whoever asked for it.
- Query-string secrets are easy to miss because they hide in
request.urlas well asrequest.queryString. Check both.