In short. A HAR (HTTP Archive) file is a JSON log that your browser writes for every network request a tab made: URLs, headers, status codes, sizes and timings. Support teams ask for it to see the problem through your eyes. Recording one takes a minute in DevTools. The catch: a raw HAR contains your session cookies and auth tokens, so scrub it before you send it.
What a HAR file is and why support asks for one
HAR stands for HTTP Archive. It is a plain text file in JSON format that your browser's developer tools produce from the network log of a single tab: every request the page sent and every response it received. The extension is .har, and the contents are human-readable JSON you can open in any text editor.
The format started in the Firebug project, reached version 1.2 and never left draft status — yet every major browser can export it and dozens of tools can read it. That is exactly why support asks for a HAR: it is a portable way to hand over the network picture without giving anyone access to your machine.
Inside the file there is a single log object with four meaningful parts:
log.versionandlog.creator— the format version and which browser wrote it;log.pages— loaded pages withonContentLoadandonLoadmarkers;log.entries— the important one: an array with one record per network request;- inside each entry —
request(method, URL, headers, cookies, POST body),response(status, headers, cookies, size, sometimes the body),timings(per-phase breakdown),serverIPAddressandtime, the total duration in milliseconds.
Who actually reads HAR files: hosting and CDN support, payment gateway engineers, front-end and back-end developers, SREs during an incident. They all look for the same things — which request failed, which returned the wrong status, and where the time went.
A HAR is not a screenshot and not a server log. It is what your browser saw: your cookies, your headers, your route to the site. That is both why it is useful for diagnosis and why it is dangerous to email around.

How to record a HAR file, step by step
The procedure is the same everywhere; only the menu labels differ. Six steps:
- Open the tab where the problem happens.
- Open developer tools — usually
F12orCtrl+Shift+I(Cmd+Option+Ion macOS). - Switch to the Network tab.
- Tick "Preserve log" (called "Persist Logs" in Firefox). Without it a redirect or a reload wipes the list.
- Reproduce the problem: reload the page, click the button, submit the form — exactly the action that breaks.
- Export the result as HAR and save the file.
Chrome
Open with F12, or the three-dot menu, then More tools, then Developer tools. Go to the Network tab. "Preserve log" sits in the toolbar next to "Disable cache" — tick both, so you capture a real load rather than a cache hit.
To export: the download-arrow icon in the Network toolbar, or right-click the request list and choose the "save all as HAR" item. Recent Chrome builds split this into two options: a sanitized export, which strips cookies and Authorization headers, and a separate one that keeps sensitive data and response bodies. Exact wording changes between versions — look for "sanitized", "sensitive data" or "with content".
Firefox
Open with F12, or the menu, then More tools, then Web Developer Tools. Go to the Network tab. The checkbox is called "Persist Logs" and lives in the filter bar above the request list.
To export: right-click any request in the list and choose "Save All As HAR". There is usually a "Copy All As HAR" next to it, handy when you want to paste into a ticket rather than attach a file.
Edge
Edge is Chromium-based, so the panel matches Chrome closely. Open with F12, or the three-dot menu, then More tools, then Developer tools. Go to the Network tab, tick "Preserve log" and "Disable cache", reproduce the issue, then export with the download-arrow icon in the Network toolbar or via right-click on the request list.
Two Edge-specific notes. First, in enterprise builds developer tools can be disabled by policy — if F12 does nothing, that is the likely reason and only an administrator can lift it. Second, Edge shows a separate list of requests per tab, so make sure you are exporting from the tab that actually reproduced the problem, not from a background one.
Safari
Developer tools are hidden by default. Open Safari settings, go to the Advanced tab and enable the option that shows web developer features; a "Develop" menu then appears in the menu bar.
Open the inspector with Cmd+Option+I or Develop, then Show Web Inspector, and switch to the Network tab. To export, use the export button (a down arrow) on the right side of the Network toolbar — it writes the whole log to a .har file.
Safari's equivalent of "Preserve log" is not present in every version and lives in the Network tab's own settings rather than the main toolbar. If you cannot find it, simply do not reload more than necessary: start the recording, reproduce the problem, export immediately. Safari on iOS cannot export a HAR by itself — connect the device to a Mac and use Safari's Develop menu to inspect it remotely.
Chromium-based browsers in general
Brave, Opera, Vivaldi, Yandex Browser and the rest of the Chromium family all ship the same DevTools. The keyboard shortcut, the Network tab, the "Preserve log" checkbox and the HAR export behave identically; only the path through the application menu is worded differently.
| Browser | Open DevTools | Keep-the-log checkbox | Export |
|---|---|---|---|
| Chrome | F12 → Network tab | Preserve log | Download-arrow icon in the Network toolbar, or right-click → save all as HAR |
| Firefox | F12 → Network tab | Persist Logs | Right-click the request list → Save All As HAR |
| Edge | F12 → Network tab | Preserve log | Download-arrow icon in the Network toolbar |
| Safari | Cmd+Option+I after enabling the Develop menu | Not in every version; in the Network tab settings | Export button on the right of the Network toolbar |
| Other Chromium browsers | F12 → Network tab | Preserve log | Same as Chrome |
The most common reason for "they sent me an empty HAR" is a missing "Preserve log" tick. Without it a redirect, a link click or a plain reload clears the request list and you export nothing. Enable it before you reproduce the problem.

What ends up inside a HAR: cookies, tokens, passwords
This is the part most instructions skip. A HAR is not anonymised telemetry. It records the content of the exchange, not just the fact of it. A raw file contains:
- Session cookies — in the
request.cookiesarray and in theCookieheader. Enough to enter your account without a password for as long as the session lives. - The
Authorizationheader — bearer tokens, JWTs, API keys, and Basic auth, which is a base64 string one decode away from your login and password. Set-Cookieheaders from responses — freshly issued session identifiers.- POST bodies in
request.postData— everything you typed into a form: address, phone number, order id, and on a sign-in page, the credentials themselves in clear text. - URL parameters — password reset tokens, one-time links and API keys whenever they travel in the query string.
- Response bodies, if the export was made "with content" — HTML and JSON with personal data: names, e-mails, amounts, documents.
- Your network path —
serverIPAddressper host, plus the full list of third-party domains the page contacted.
Sending an unprocessed HAR to support means handing over access to your session. The file lands in a ticket system, sits in mailboxes, gets forwarded between staff and stays there for years. Treat a raw HAR as the equivalent of a password.
How to record a HAR without sensitive data
- Do not sign in inside the same recording. Log in first, then open DevTools, start capturing and reproduce only the failure. That keeps the sign-in POST with your credentials out of the file.
- Capture the minimum. One action, one HAR. A short recording contains less and is far easier for support to read.
- If the problem reproduces logged out, capture it in a private window on an anonymous session. That is the cleanest option: almost no cookies, no tokens.
- Do not save response bodies unless asked. Response bodies (
response.content.text) are a separate thing from headers. An export "with content" embeds whole HTML and JSON payloads as base64: the file balloons and starts carrying whatever was on screen. Performance diagnosis does not need bodies at all — timings and sizes live in other fields. - Do not rely on the browser sanitising for you. Recent Chrome versions do strip cookies and
Authorizationfrom the default export, but that behaviour is not in every version and not in every browser. Always verify the file yourself — the command is below.

How to scrub a HAR before sending it
A HAR is JSON, so it can be edited programmatically. The handiest tool is jq. The command below empties cookies in requests and responses, drops auth headers and Set-Cookie, and wipes POST bodies and response bodies:
# Scrub a HAR: cookies, auth headers, request and response bodies
jq --arg drop "cookie,authorization,proxy-authorization,x-api-key,x-auth-token,x-csrf-token" '
($drop | split(",")) as $d
| .log.entries |= map(
.request.cookies = []
| .response.cookies = []
| .request.headers |= map(select((.name | ascii_downcase) as $n | $d | index($n) | not))
| .response.headers |= map(select((.name | ascii_downcase) != "set-cookie"))
| (if .request.postData then .request.postData.text = "[removed]"
| .request.postData.params = [] else . end)
| (if .response.content then .response.content |= del(.text) else . end)
)' raw.har > clean.har
Then verify. The first command must print nothing, the second must print zero:
# 1. Any cookie/authorization headers left?
jq -r '.log.entries[].request.headers[]?.name' clean.har | tr 'A-Z' 'a-z' | grep -E '^(cookie|authorization)$' | sort -u
# 2. Any response bodies left?
jq '[.log.entries[].response.content.text // empty] | length' clean.har
# 3. Eyeball it: grep the whole file for suspicious strings
grep -o -i -E '(bearer [a-z0-9._-]{10,}|sessionid|phpsessid|password)' clean.har | sort -u
Check query strings separately. If the site passes a token through the URL (?token=…, ?key=…), it stays in request.url and no amount of header scrubbing removes it. Those URLs are easiest to edit by hand in a text editor.
If you have already sent a HAR captured on a live session, treat that session as compromised. Sign out on all devices — any decent service has a "terminate all sessions" button — revoke API keys that appeared in headers, and change the password if the recording contained a sign-in.
An important consequence: a scrubbed HAR is just as informative to analyse as a raw one. Timings, status codes, sizes (bodySize, headersSize), caching and compression headers, request order — all of that survives. The secrets live in different fields. So "we need a HAR with content" is a request worth questioning: for a performance investigation it is almost never required.
How to open and read a HAR file
Three practical options:
- Back into DevTools. Open the Network tab and drag the
.harstraight onto the request list — the browser redraws the familiar waterfall with all timings. Fastest way to look at it with your eyes, and it works offline. - An online analyser. Our HAR analyser parses the file and surfaces bottlenecks immediately: slowest requests, redirect chains, heavy uncompressed resources.
- A text editor or
jq. When you have one specific question ("which request returned 502?"), a one-liner beats scrolling a UI.
What to look at first:
- The waterfall — the visual timeline. Look for long bars and staircases, where the next request only starts after the previous one finished.
- Status codes — anything that is not 2xx: 3xx (redundant redirects), 4xx (broken links, expired auth), 5xx (server-side failure).
- Timings — the per-phase breakdown in the table below.
- Sizes and compression — a
bodySizein the hundreds of kilobytes with noContent-Encodingheader means the resource ships without gzip or brotli.
Field in timings | What it measures | What a large value suggests |
|---|---|---|
blocked | Time queued in the browser before the request starts | Hit the per-host connection limit, or a slow proxy or browser extension is in the way |
dns | Domain name resolution | Slow or distant resolver, cold cache, too many third-party domains |
connect | TCP connection setup | Server is far away, packet loss, no keep-alive |
ssl | TLS handshake (included in connect) | Long certificate chain, no session resumption, weak server hardware |
send | Pushing the request to the server | Large POST body or a narrow uplink on the client |
wait | Waiting for the first byte — this is TTFB at request level | Slow backend: heavy database queries, no caching, external API calls inside the handler |
receive | Downloading the response body | Resource is large or uncompressed, narrow link, no CDN |
A value of -1 in any field means "not applicable" — for example dns and connect are -1 when the connection was reused. The phases add up to entry.time.
What a HAR is not
A HAR describes the network, and only the network. It will not show you:
- JavaScript execution. Long tasks, a blocked main thread, wasteful re-renders — that is the Performance profiler, not a HAR.
- Rendering and layout. Layout shifts, time to paint the largest element, responsiveness to a click — those are Core Web Vitals, and a HAR does not measure them.
- A score. A HAR is raw data, not a report. For a synthetic audit with recommendations use a website speed testing tool.
- What happened on the server. A long
waittells you the backend was slow, not why — that answer lives in application logs.

How to find the cause of a slow page in a HAR
Start with two commands: one shows who thought the longest, the other who weighs the most.
# Top 20 requests by server think time (wait ~ TTFB)
jq -r '.log.entries[] | [(.timings.wait | floor), .response.status, .request.url] | @tsv' page.har | sort -rn | head -20
# Heaviest responses and how they are compressed
jq -r '.log.entries | sort_by(-(.response.bodySize)) | .[:15][]
| [ .response.bodySize,
(.response.content.mimeType // "-"),
(first(.response.headers[] | select(.name | ascii_downcase == "content-encoding") | .value) // "none"),
.request.url ] | @tsv' page.har
# Redirect chains
jq -r '.log.entries[] | select(.response.status >= 300 and .response.status < 400)
| [.response.status, .request.url, (.response.redirectURL // "-")] | @tsv' page.har
# URLs requested more than once
jq -r '.log.entries[].request.url' page.har | sort | uniq -c | sort -rn | awk '$1 > 1'
| What you see in the HAR | Likely cause | What to do and how to verify |
|---|---|---|
Long wait (hundreds of ms to seconds) on the HTML document itself | Slow backend: heavy queries, no page cache, an external call inside the handler | Add server-side caching, profile the backend. Measure from outside with a speed check |
Dozens of requests with a long blocked | Per-host connection limit reached, typical of HTTP/1.1 | Move to HTTP/2 or HTTP/3, bundle small files. Check the protocol and headers with the HTTP header analyser |
| A 301 → 302 → 200 chain at the start of the recording | Redundant redirects: http to https to www to trailing slash | Collapse to a single hop. Verify the chain with the URL and redirect check |
Hundreds of kilobytes with no Content-Encoding | gzip or brotli not enabled on the server | Turn on compression for text types. Verify with the HTTP header analyser |
| Lots of third-party domains near the start of the waterfall | Blocking third-party scripts: analytics, chat widgets, fonts | Load them with async or defer, after the main content |
| The same URL repeated dozens of times | No caching, a broken retry loop, or a loop in client code | Set Cache-Control and ETag, cap retries |
Long dns across many domains | Slow resolver or cold cache | Reduce the number of third-party domains. Verify with a DNS lookup and a ping check |
Long connect and ssl | Distant server, packet loss, expensive handshake | Use a CDN and keep-alive, inspect the route with traceroute |
HAR recording problems and how to fix them
The file is empty or has two entries
Almost always a missing "Preserve log" tick, with a reload wiping the list. The other cause is opening DevTools after the page had already loaded: the browser only records while the panel is open. The correct order is open DevTools, enable Preserve log, and only then reload.
The recording stops at a redirect
Same reason. Every navigation is a fresh page load, and without Preserve log the list is cleared. It shows up most often on sign-in and checkout flows, where several hops happen in a row.
The file is enormous
A 50–200 MB HAR usually means an export "with content" on a heavy page: response bodies sit inside as base64, which inflates them by roughly a third over the original bytes. What to do:
# How many entries, and how big
jq '.log.entries | length' page.har
du -h page.har
# Keep only your own domain — usually shrinks the file several times over
jq '.log.entries |= map(select(.request.url | test("^https://example\.com/")))' page.har > small.har
# Or drop response bodies while keeping every diagnostic field
jq '.log.entries |= map(if .response.content then .response.content |= del(.text) else . end)' page.har > light.har
The file will not open and the analyser complains about JSON
Usually the file is truncated: the browser or tab died mid-export, or the recording was simply too long. The tell is that the file does not end with }. Check integrity with jq empty page.har — if the JSON is broken, jq reports the offset. The only fix is a fresh, shorter recording. Another common cause: the file was sent through a messenger that renamed or repackaged it — try the original.
The request you need is missing
Check three things. First, filters in the Network panel: with a type filter on (XHR only, for instance) the export usually still contains everything, but you will not see it on screen — search the file, not the UI. Second, the request may have come from another tab, an iframe or an extension; a HAR only covers the current tab. Third, the response may have come from cache: tick "Disable cache" and try again.
How to analyse a HAR with our tools
Upload the file to the HAR analyser: it parses log.entries, breaks the time down by phase, surfaces the slowest requests, redirect chains, error responses and heavy uncompressed resources. A file scrubbed of cookies and tokens analyses just as fully — scrubbing does not touch timings or sizes.
What to pair it with:
- Speed check — a synthetic measurement from outside, to tell whether the problem is everyone's or only yours;
- URL check — status codes and the redirect chain for one specific address;
- HTTP header analyser — caching, compression, protocol. More detail in the article on HTTP headers;
- Ping and traceroute — when the HAR shows long
connectanddnsphases. How to read the numbers is covered in the guide to checking ping.
Frequently asked questions
Is a HAR file dangerous?
A raw one is. It carries your session cookies, the Authorization header and form bodies — enough to enter your account without a password until the session expires. A scrubbed HAR is safe and still perfectly usable for performance diagnosis.
How do I open a HAR file offline?
Drag it onto the Network tab of any browser's developer tools and it renders as a normal waterfall. Nothing is uploaded anywhere.
How is a HAR different from a screenshot of the Network panel?
A screenshot is a picture; a HAR is data. With a HAR you can sort requests, sum sizes, find a specific header and feed the file to an analyser. Support almost always needs the file itself.
Why is there no response body in my HAR?
Because the export was the plain one rather than "with content". For performance work that is fine and in fact preferable: timings, statuses and sizes are all there, and no personal data is.
Can I record a HAR on a phone?
Not directly in a mobile browser. The standard route is remote debugging: connect the device to a computer and capture from desktop Chrome for Chromium-based Android browsers, or from Safari on macOS for Safari on iOS. The Network panel then works as usual.
How many entries is normal for one page?
A typical page makes anywhere from a few dozen to a couple hundred requests. A thousand or more is a signal to look for a loop in client code or an avalanche of third-party scripts.
Support asks for a HAR "with content" — should I agree?
Ask which request they actually need. Often one specific response body is enough rather than the whole page. If bodies really are required, capture the recording on a test account or on data you do not mind sharing.
Checklist before you send a HAR
- DevTools opened before the reload, "Preserve log" enabled.
- The recording contains only the reproduction — no sign-in, no unrelated clicking.
- Exported without response bodies unless they were explicitly requested.
- File run through
jq: cookies emptied,AuthorizationandSet-Cookieremoved, POST bodies wiped. - Verification done: grepping for
cookie,authorization,bearer,passwordreturns nothing. - Tokens in query strings replaced by hand.
- File size reasonable — unrelated domains filtered out.
jq empty file.harruns clean, so the JSON is intact.- The file reopens in the Network panel and contains the request in question.
- If the recording used a live session, that session has been terminated and API keys revoked.