Inspect an HTTP Request: curl, Raw Messages and HAR Files

Inspect an HTTP Request: curl, Raw Messages and HAR Files

Format a Copy as cURL, parse a raw HTTP request or response and read a HAR export in your browser: nothing is sent, nothing is replayed.

07.10.2026
17 min read
Share this article:
HTTPcURLHARDebuggingTutorial

One request, three ways to read it: a cURL command, a raw message, a HAR file

When an API integration misbehaves, the evidence is almost always an HTTP exchange you can copy out of DevTools. The catch is the shape it arrives in: Chrome's Copy as cURL is a single very long line, a pasted response is a block of headers with a body underneath, and a HAR export is a JSON file with hundreds of entries. The cURL formatter turns a Copy as cURL or Copy as fetch into readable multi-line cURL, or into HTTP/1.1, a .http file, JavaScript fetch, Python requests, Go or PHP. The HTTP message parser reads a raw request, a response or a header block, and keeps duplicate headers and pretty-prints a JSON body. The HAR inspector opens a whole recorded session, lets you filter it down to the failing call, and copies that call back out as cURL. All three work on pasted text in your browser: FastMinify never sends the request, never fetches the URL you pasted, and has no runner or proxy, so a token in the paste stays in your tab. This guide covers which tool answers which question, how to debug a request that works in the browser but not in your script, the mistakes that leak credentials or mislead you, the anatomy of an HTTP message, and the same checks in a terminal and in Node. The tools sit in the HTTP tools hub.

cURL formatter: reads Chrome Copy as cURL and Copy as fetch (parsed as text, never executed), a single-request .http file or a raw HTTP/1.1 request. Output as cURL multi-line or one-line, HTTP/1.1, .http, JavaScript fetch, Python requests, Go net/http or PHP curl
The Sanitize secrets option (off by default in the formatter) drops the Cookie, Authorization, Referer and sec-ch-* headers and the -u and -b values from what you copy
HTTP message parser: tells a request, a response and a headers-only block apart, accepts LF or CRLF line endings, keeps duplicate headers and can pretty-print a JSON body. It also cleans up pasted <code>curl -v</code> output (the lines starting with > and <)
HAR inspector: opens a .har or .json file up to 50 MB (a warning above 20 MB), filters entries by type, method, status and URL, and copies one call as cURL or HTTP/1.1. Its Sanitize secrets option is on by default
No Send button anywhere: the request is never executed, so there is no CORS error to fight and no side effect on your API
Everything runs in the tab: no pasted command, header or HAR is uploaded

Which tool answers which question

Fitting uses

These three tools cover reading, translating and sharing an HTTP exchange. They do not run it.

Making a DevTools Copy as cURL readable before it goes into a ticket, a README or a code review: one header per line instead of one wall of text
Translating the same request into fetch, Python requests, Go or PHP to reproduce a call in the language of your project
Checking a response a colleague pasted: status line, duplicate headers (several Set-Cookie lines, Vary) and a JSON body, in the <a href="/en/http-message-parser" class="text-primary hover:underline">HTTP message parser</a>
Pasting the output of <code>curl -v</code> and getting the request or the response back without the <code>*</code> log lines
Finding the one failing call among hundreds in a HAR with the Errors pill, then copying it as cURL to retry from a terminal
What belongs elsewhere

Each tool stops where another one starts. Hand over to the neighbouring tools or to your own code.

Replaying a request: there is no runner. Paste the cURL into a terminal, Postman or your test suite
Breaking a URL into protocol, host, path and query rows, or stripping tracking parameters: the <a href="/en/url-parser" class="text-primary hover:underline">URL parser</a>
Editing a <code>Cookie</code> or <code>Set-Cookie</code> header and checking SameSite or the <code>__Host-</code> prefix: the <a href="/en/cookie-header-editor" class="text-primary hover:underline">cookie editor</a>
Reading the claims of a Bearer token you found in an Authorization header: the <a href="/en/jwt-decode" class="text-primary hover:underline">JWT decoder</a>, and the <a href="/en/blog/jwt-encode-decode-online-guide" class="text-primary hover:underline">JWT guide</a> for what a decoded token does and does not prove
Validating the API contract itself rather than one call: the <a href="/en/validate-openapi" class="text-primary hover:underline">OpenAPI validator</a> and the <a href="/en/blog/validate-openapi-swagger-online-guide" class="text-primary hover:underline">OpenAPI validation guide</a>

Debugging an API call from a copied request

It works in the browser but not in your script

Paste the Copy as cURL into the cURL formatter and read it line by line: it contains everything the browser added, not only what your code needs.

Browser-added headers such as <code>sec-ch-ua</code>, <code>accept-language</code>, <code>origin</code>, <code>referer</code> and the whole <code>cookie</code> line are rarely required. Remove them one at a time until the call fails, and the last one you removed is the one that mattered
A cookie-authenticated call fails from a script because the cookie is missing, not because the code is wrong: the session lives in <code>Cookie</code>, while an API client usually sends an <code>Authorization</code> header
The method is inferred when you do not state it: a body with no <code>-X</code> means POST, no body means GET. A GET that carries a body is a sign something was lost in the copy
A <code>Content-Type</code> that does not match the body (JSON sent as <code>application/x-www-form-urlencoded</code>, or the other way round) is the classic cause of a 400 or 415
A trailing <code>--compressed</code> means the browser sent <code>Accept-Encoding</code> and decoded the answer for you. Your client has to do the same, or the body looks like garbage
Reading a response: status, headers, body

Paste the response into the HTTP message parser. The start-line, the header table and the body are laid out separately.

The status class tells you who to blame first: 4xx means the request is at fault, 5xx the server, 3xx a redirect you have to follow (RFC 9110). Check <code>Location</code> on a 3xx
<code>WWW-Authenticate</code> on a 401 names the scheme the server expects, and <code>Retry-After</code> on a 429 or 503 says how long to wait
Duplicate headers are kept as separate rows. Two <code>Set-Cookie</code> lines are two cookies, not a mistake, and a repeated <code>Vary</code> is legal
The Content-Type header decides how the body should be read: a JSON error page served as <code>text/html</code> usually comes from a proxy or a gateway, not from your API
Chunked bodies are not reassembled. If the body starts with a hexadecimal size on its own line, you pasted the raw chunked transfer encoding, so copy the decoded body from DevTools instead
Finding the failing call in a HAR

Open the export in the HAR inspector and narrow the list before you read any detail.

The Total requests, Total size, Load time and Errors counters sit above the list. Errors counts the entries with a status of 400 or more
A request that never got a response is recorded with status 0 (blocked by an extension, a DNS failure, a CORS preflight that failed). It is not counted as an error, so scroll the unfiltered list as well
The XHR pill groups fetch and XMLHttpRequest calls. Add a text filter on <code>/api/</code> to drop fonts, images and tracking scripts
Select a row to read the request headers, the response headers, the payload and a preview of the response body, then Copy cURL to retry it
A HAR exported from Chrome 130 or later is sanitized by default and omits Cookie, Set-Cookie and Authorization. A missing Authorization header in the file does not prove the browser did not send it

Four mistakes that leak a credential or send you down the wrong path

Sharing a copied request with its credentials still in it

A Copy as cURL from an authenticated session carries your Cookie and Authorization headers, and anyone who reads the ticket can replay the call as you. The Sanitize secrets switch removes Cookie, Authorization, Referer, sec-ch-*, and the -u and -b values from the copy. It is off by default in the formatter and in the parser's Copy as cURL, and on by default in the HAR inspector. It works on headers only: a token in the query string (?access_token=), in the request body, in a custom header such as X-Api-Key or in a response body stays exactly where it was. Read the output before you paste it, and rotate anything that already left your machine. The secrets scanner helps to spot a key you did not notice.

Posting a Copy as cURL in a public issue because the Authorization header is "just a test token"
Assuming Sanitize secrets removes tokens from the URL or the body
Sending a HAR to a vendor without checking the response bodies, which can hold personal data
Leaving a long-lived key in a cURL kept in a wiki or a shell history
Sanitize, then read the result once more: query strings, bodies and custom headers are yours to clean. Rotate any credential that was shared.
Reading the HAR counters as page performance

The HAR inspector's Load time is the duration of the longest single entry, not the time the page took to load, and Total size adds up the body sizes recorded in each entry. They answer "which call was slowest" and "how much data moved", not what Lighthouse measures. Requests run in parallel, so the sum of the times is not the page time either. Cached responses and compressed bodies make the size figures differ from what you see in the Network panel footer.

Quoting Load time from the inspector as the page load time
Comparing the HAR total size with a Lighthouse transfer size and calling one of them wrong
Ignoring requests with status 0 because the Errors counter is 0
Judging a performance problem from one HAR recorded with the cache disabled and an extension running
You optimise the wrong endpoint, or report an error rate of zero while a request is silently blocked.
Expecting a faithful replay of what you pasted

The formatter understands the DevTools subset of cURL, and ignores the rest without complaint. Flags such as -L, -s, -k, --insecure, -v and -o are dropped from the output, so a command that relied on -L to follow a redirect or -k to skip certificate checks loses that behaviour when you translate it. Some inputs are refused with a clear message: multipart uploads (-F), -T file uploads, @file bodies, REST Client {{variables}}, multi-request .http files, axios snippets, and fetch calls that use template literals or unbound variables. A pasted response is not a request, so there is nothing to turn into cURL.

Translating a cURL that used -L and expecting the generated fetch to follow redirects differently
Pasting a multipart upload and wondering why the formatter refuses it
Copying a request as cURL from a response-only paste
Treating the generated snippet as production code instead of a starting point
Read the generated code once, add timeouts, retries and error handling yourself, and keep the secrets in environment variables.
Changing the body without noticing

Several outputs re-indent a JSON body: the multi-line cURL, the HTTP/1.1 and .http outputs, JavaScript fetch, and Python requests, which turns it into a json= literal. The one-line cURL keeps the body as you pasted it. That is harmless for most APIs, and fatal when the server checks a signature computed over the exact bytes, as webhook endpoints often do: one added space and the HMAC no longer matches. The hash and HMAC guide explains how those signatures are built.

Replaying a signed webhook body after it went through a pretty-printer
Comparing a generated snippet with the original byte for byte
Using the multi-line output as a fixture for a signature test
Pasting the multi-line output into a test that compares the body as a string
For a signed body, keep the original text, use the one-line cURL, and compute the signature on the exact string you send.

Anatomy of an HTTP message and where each tool looks

Start-line, headers, blank line, body

An HTTP/1.1 message is plain text (RFC 9112), and its meaning is defined in RFC 9110. Request and response share the layout.

Request start-line: <code>POST /orders HTTP/1.1</code> carries the method, the target and the protocol version. A request must have a <code>Host</code> header
Response start-line: <code>HTTP/1.1 201 Created</code> carries the version, a three-digit status code and a reason phrase that clients ignore
Headers: one <code>Name: value</code> per line, names are case-insensitive, and the same name may appear several times
A blank line ends the headers. Whatever follows is the body, which may be empty (a GET, a 204)
Folded header lines (a continuation that starts with a space) are joined into one value by the parser
The same request in four shapes

The tools convert between these, which is why one copied request can end up as any of them.

Copy as cURL: <code>curl URL -H "Name: value" --data-raw BODY --compressed</code>, with the method added by <code>-X</code> when it is not GET
Copy as fetch: <code>fetch(url, { method, headers, body })</code>, a JavaScript literal the formatter reads as text and never evaluates
Raw HTTP/1.1: the start-line, a Host header and the other headers, then the body. The <code>.http</code> format of REST Client extensions is the same with the full URL on the first line
HAR entry: a JSON object with <code>request</code> (method, url, headers, postData) and <code>response</code> (status, headers, content), plus a <code>time</code> in milliseconds. HAR 1.2 is described in a W3C draft specification
Python, Go and PHP outputs are the same call written with requests, net/http and the curl extension
Status classes and the headers to read first

A short reading order saves most debugging sessions.

1xx informational, 2xx success, 3xx redirection, 4xx client error, 5xx server error (RFC 9110). The parser shows the class of a response as a coloured badge
401 means "not authenticated" and comes with <code>WWW-Authenticate</code>; 403 means authenticated but not allowed; 429 means rate limited and often has <code>Retry-After</code>
<code>Content-Type</code> and <code>Content-Length</code> (or chunked transfer) describe the body, <code>Accept</code> and <code>Accept-Encoding</code> describe what the client can read
<code>Cache-Control</code>, <code>ETag</code> and <code>Age</code> explain why a response is stale or not fresh, and <code>Set-Cookie</code> and <code>Location</code> change what the client does next
The same classes appear as the Status filter in the HAR inspector, with an Errors shortcut for 400 and above

The three tools, step by step

Format a Copy as cURL with the cURL formatter

The cURL formatter reformats as you paste, with no Send button. In Chrome, right-click the request in the Network tab, then Copy, then Copy as cURL (bash).

1

Paste the command

A Copy as cURL, a Copy as fetch, a single-request .http file or a raw HTTP/1.1 request are all detected. Line continuations are joined for you.

2

Pick the language and the layout

cURL, HTTP/1.1, .http, JavaScript fetch, Python requests, Go net/http or PHP curl, in multi-line or one-line layout.

3

Switch on Sanitize secrets before you share

It strips Cookie, Authorization, Referer, sec-ch-* and the -u and -b values from the copy. Query strings and bodies are untouched.

4

Copy or download the result

Each language has its own file name (formatted-curl.sh, formatted-request.http, formatted-requests.py and so on). A parse error names what was refused and why.

Read a raw request or response with the HTTP message parser

The HTTP message parser lays a pasted message out as a start-line, a header table and a body.

1

Paste the message

A request, a response, a bare header block or the output of curl -v. LF and CRLF line endings both work.

2

Check the detected kind

The tool labels it HTTP request, HTTP response or Headers only, then shows the number of headers. Duplicates stay as separate rows.

3

Read the body

A JSON body is pretty-printed by default, with a switch to see it raw. Chunked bodies are not reassembled.

4

Copy a request as cURL

This needs a request with an absolute URL or a Host header. With only a Host header, the scheme is guessed: http for localhost, 127.0.0.1 or port 80, https otherwise. The Sanitize copy switch is off by default.

Explore a session with the HAR inspector

The HAR inspector reads the file in your tab and never fetches the URLs it lists.

1

Export the HAR

In the Network panel, use the download icon (Export HAR). Chrome, Firefox, Edge, Charles and HTTP Toolkit exports open, up to 50 MB; above 20 MB filtering can feel slow.

2

Drop the file or paste the JSON

A .har or .json file, or the HAR 1.2 JSON itself. The sample button loads a small capture to try the filters.

3

Narrow the list

Type pills (XHR, JS, CSS, Img, Media, Font, Doc, Other, Errors), a method, a status class and a "URL contains" search. The type comes from the entry when Chrome records it, or from the response MIME type.

4

Open a call and extract it

Read the request and response headers, the payload and a body preview. Copy cURL, Copy HTTP/1.1 or Copy full URL, or download the file; with Sanitize secrets on, the download is written as sanitized.har with the Cookie, Set-Cookie, Authorization, Referer and sec-ch-* headers and the cookie arrays removed.

What the tools do not do

A few limits to know before you rely on them.

No request is ever sent or replayed: there is no runner, no proxy and no Send button
No multipart (-F) or file (-T, @file) bodies, no REST Client {{variables}}, no axios snippets, no multi-request .http files
cURL flags such as -L, -k, -s and -o are ignored in the output
No reassembly of chunked bodies in the message parser
The HAR inspector does not draw a waterfall or compute page timings
Sanitize secrets works on headers and cookie arrays, not on URLs, bodies or custom headers

The same checks in a terminal and in Node

curl: headers only, or the whole exchange

When you can run a command, curl shows the same data the parser lays out. Keep tokens in environment variables, not in the command you paste into a ticket.

Basic example

# Response headers only: -D - writes them to stdout, -o /dev/null drops the body curl -sS -D - -o /dev/null https://api.example.com/orders/42 \ -H "Authorization: Bearer $API_TOKEN" # The full exchange: lines starting with > are sent, < received, * is curl's own log curl -v https://api.example.com/orders/42 2>&1 | head -40
Node: filter a HAR without opening a browser

A HAR is JSON, so a few lines list the failing calls. Unlike the inspector's Errors counter, this filter also keeps status 0.

Basic example

import { readFileSync } from 'node:fs' const har = JSON.parse(readFileSync('session.har', 'utf8')) // Status 0 means no response at all (blocked, DNS failure, CORS): keep it in the list const failed = har.log.entries .filter((e) => e.response.status >= 400 || e.response.status === 0) .map((e) => ({ method: e.request.method, status: e.response.status, url: e.request.url, ms: Math.round(e.time), })) console.table(failed)
Node: reproduce a copied fetch call

Take the Copy as fetch output, move the secret to an environment variable and log what comes back. The generated snippets from the formatter are a starting point of the same kind.

Basic example

// The call you copied as fetch(), with the secret read from the environment const res = await fetch('https://api.example.com/orders', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + process.env.API_TOKEN, }, body: JSON.stringify({ sku: 'A-1042', qty: 2 }), }) console.log(res.status, res.statusText) console.log(res.headers.get('content-type')) console.log(res.headers.getSetCookie()) // every Set-Cookie line, Node 20+ console.log(await res.text())
FastMinify HTTP tools

Nothing to install and nothing sent: the cURL formatter, the HTTP message parser and the HAR inspector cover reading, translating and sharing a request. The trade-offs: no replay, a subset of cURL, no timing waterfall, and a sanitizer that only knows headers. For a real test, run the call in a terminal, Postman or your own test suite; to check a spec rather than a call, use the OpenAPI validator.

Conclusion

An HTTP exchange is just text, and most of debugging it is reading it in the right shape. The cURL formatter makes a Copy as cURL readable and portable, the HTTP message parser lays out a raw request or response with its duplicate headers and JSON body, and the HAR inspector narrows a recorded session down to the call that failed. None of them sends anything: you stay in control of what leaves your browser, which matters because these pastes carry cookies and tokens. Sanitize before you share, remember that it only cleans headers, and read the counters for what they are. The HTTP tools hub gathers the neighbouring tools for URLs and cookies.

Read a Copy as cURL line by line and remove the headers the browser added before you reproduce a call
Sanitize secrets before sharing, then clean the query string, the body and custom headers by hand
In a HAR, count status 0 as a failure and read Load time as the slowest call, not as the page time
Keep the original body when a signature covers it: re-indenting JSON changes the bytes
Use a terminal or a test suite to replay a call: the browser tools read and translate, they never send
Share this article
Share this article: