Debugging an API call or web request often depends on seeing the actual HTTP exchange instead of only the response body. Connection reuse, redirects, TLS negotiation, request headers, and status-line changes can all affect the result long before application logic becomes the real problem.
cURL exposes different layers of that exchange with a small set of flags. –verbose shows connection notes plus sent and received protocol lines on stderr, –head reduces noise when only headers matter, and –trace-ascii with –trace-time records a timestamped transfer log when request bodies or low-level timing need inspection.
Verbose and trace logs can reveal cookies, tokens, POST bodies, proxy-added headers, and local configuration effects such as entries from ~/.curlrc. The hostnames, request IDs, and payload values in the examples below are masked to preserve the shape of production traffic without exposing live identifiers. Keep captures in a dedicated workspace, use fixed values when reproducible output matters, and redact or remove the files after the request has been analyzed.
Steps to debug HTTP requests with verbose cURL output:
- Create a clean workspace for verbose logs, trace files, and redacted copies.
$ mkdir -p ~/curl-http-debug $ cd ~/curl-http-debug
Keeping debug artifacts in one directory makes it easier to inspect, compare, and remove sensitive captures after the session ends.
- Capture one full request exchange with –verbose while discarding the response body.
$ curl --silent --show-error --verbose --user-agent "curl/8.x" --output /dev/null "https://api.example/v1/health" 2> curl-verbose.log $ sed -n '1,16p' curl-verbose.log * Host api.example:443 was resolved. * IPv6: 2001:db8:52::25, 2001:db8:52::26 * IPv4: 198.51.100.24, 198.51.100.25 * Trying [2001:db8:52::25]:443... * Connected to api.example (2001:db8:52::25) port 443 * ALPN: curl offers h2,http/1.1 ##### additional TLS handshake lines masked ##### > GET /v1/health HTTP/2 > Host: api.example > User-Agent: curl/8.x > Accept: */* > * Request completely sent off < HTTP/2 200 < date: Sun, 29 Mar 2026 03:39:11 GMT < content-type: application/json
–output /dev/null keeps the response body out of the terminal so the saved log stays focused on the transport exchange.
The host and path in this sample are masked; keep the command shape and replace them with the actual endpoint being debugged.
Related: How to save cURL output to a file
- Filter the saved verbose log to isolate the sent and received HTTP header blocks.
$ grep -E '^(>|<)' curl-verbose.log > GET /v1/health HTTP/2 > Host: api.example > User-Agent: curl/8.x > Accept: */* < HTTP/2 200 < date: Sun, 29 Mar 2026 03:39:11 GMT < content-type: application/json < cache-control: no-store < x-request-id: req_01JQF9Z7Q2A6M4R5N8S1T3V6WX
In –verbose output, > marks headers sent by cURL, < marks headers received from the server, * marks connection notes, and { or } mark transfer data when present.
- Repeat the request with –head when only the header exchange and status metadata matter.
$ curl --silent --show-error --head --verbose --user-agent "curl/8.x" --output /dev/null "https://api.example/v1/health" 2> curl-head.log $ grep -E '^(>|<)' curl-head.log > HEAD /v1/health HTTP/2 > Host: api.example > User-Agent: curl/8.x > Accept: */* < HTTP/2 200 < date: Sun, 29 Mar 2026 03:39:11 GMT < content-type: application/json < cache-control: no-store < x-request-id: req_01JQF9Z7Q2A6M4R5N8S1T3V6WX
–head reduces the exchange to request and response headers, which is useful when checking redirects, cache headers, authentication challenges, or content negotiation without mixing in a response body.
- Switch to –trace-ascii with –trace-time when the request body or transfer timing must be inspected.
$ curl --silent --show-error --trace-time --trace-ascii curl-trace.log --output response.json --user-agent "curl/8.x" --request POST --header "Content-Type: application/json" --data '{"mode":"audit"}' https://api.example/v1/audit $ sed -n '1,18p' response.json { "args": {}, "data": "{\"mode\":\"audit\"}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Content-Length": "16", "Content-Type": "application/json", ##### additional request metadata masked ##### }, "json": { "mode": "audit" }, "method": "POST"An echo endpoint makes it possible to compare the wire trace with what the server actually received, which is useful when debugging missing headers, incorrect payload encoding, or unexpected method changes.
- Review the trace markers that prove the exact request body and final response status.
$ grep -E 'Send header|Send data|Recv header|POST /v1/audit|Host: api.example|User-Agent: curl/8.x|Content-Type: application/json|\\{\"mode\":\"audit\"\\}|HTTP/2 200' curl-trace.log 11:38:42.563309 => Send header, 131 bytes (0x83) 0000: POST /v1/audit HTTP/2 0017: Host: api.example 002a: User-Agent: curl/8.x 004d: Content-Type: application/json 11:38:42.563336 => Send data, 16 bytes (0x10) 0000: {"mode":"audit"} 11:38:42.854452 <= Recv header, 13 bytes (0xd) 0000: HTTP/2 200 11:38:42.854512 <= Recv header, 32 bytes (0x20) 0000: content-type: application/jsonThe trace is complete when the expected request line, request body, and final HTTP status appear in the same capture.
- Redact or remove saved diagnostics before sharing them outside the debugging session.
$ sed -E 's/^(> Authorization:).*/\\1 ***REDACTED***/' curl-verbose.log > curl-verbose-redacted.log $ rm -f curl-verbose.log curl-head.log curl-trace.log response.json
Verbose and trace captures can expose bearer tokens, cookies, query strings, and raw request bodies, so scrub them before sending them to another team or attaching them to tickets.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
