Tutorial

cURL headers: send, show and debug HTTP headers

Send, change, remove and read HTTP headers with curl. Every command run on 10 October 2026, with proxy headers and redirects tested on a stub proxy.

HProxy Team··Updated October 10, 2026·10 min read
HProxy.Tutorial

Skip the dead lists.

Our free proxy list re-checks every exit every few minutes across 100+ countries, with a live last-checked time, so you copy IPs that worked moments ago, not a stale text dump.

Open the free proxy list→

To send a header with curl, add -H 'Name: value'. To read the headers a server sends back, use -I for the headers alone or -i for headers and body. -v shows your own request headers as well. The same -H replaces a header curl sets itself, and -H 'Name:' removes it.

We ran every command on this page on 10 October 2026, with curl 8.5.0 on Ubuntu 24.04. httpbin.org/headers answered with the headers it received, and example.com supplied the response headers. A stub proxy and a small echo server on our own server showed which headers reach a proxy and which survive a redirect.

How do you send a header with curl?

You need curl in a terminal; What is cURL? shows how to check your version. Each step below shows the command and the answer httpbin gave.

Terminal capture of the header tests, 10 October 2026. httpbin.org/headers reports the default headers Accept, Host and User-Agent curl/8.5.0, plus X-Amzn-Trace-Id. With -H the header X-Request-Source arrives. -H 'User-Agent: my-tool/1.0' replaces the agent. -H 'User-Agent:' and -H 'Accept:' leave only Host and X-Amzn-Trace-Id. -H 'X-Empty;' arrives with an empty value. -H @headers.txt sends X-One and X-Two.
Our own run on our server, 10 October 2026, 19:51 UTC: curl 8.5.0 on Ubuntu 24.04, captured off-screen. Each command is printed before its output, then its exit code.

Step 1: see what curl sends by default

curl -s https://httpbin.org/headers
{
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/8.5.0",
    "X-Amzn-Trace-Id": "Root=1-6aca971f-0e31a64870d682f756ca89ba"
  }
}

curl sent three headers: Host, User-Agent with its own name and version, and Accept: */*. It did not send X-Amzn-Trace-Id. AWS documents that header as one its load balancers add before passing a request on, so it tells you about httpbin's side, not yours.

Step 2: add a header

curl -s -H 'X-Request-Source: docs-test' https://httpbin.org/headers

The header arrived as "X-Request-Source":"docs-test". Repeat -H for every header you need. Header names are case-insensitive under RFC 9110, so x-request-source works the same.

Step 3: replace a header curl sets itself

curl -s -H 'User-Agent: my-tool/1.0' https://httpbin.org/headers

When your header has the name of one curl would send, the manual says curl uses yours instead. httpbin saw "User-Agent":"my-tool/1.0". For the common ones there are shortcuts: -A sets User-Agent, -e sets Referer and -b sends cookies.

Step 4: remove a header

curl -s -H 'User-Agent:' -H 'Accept:' https://httpbin.org/headers

A name with a colon and nothing after it removes the header. httpbin received only Host and its own trace header.

Step 5: send a header with an empty value

curl -s -H 'X-Empty;' https://httpbin.org/headers

A semicolon instead of the colon sends the header with no value, and httpbin showed "X-Empty":"". The two forms are easy to mix up: the colon removes, the semicolon sends empty.

Step 6: read headers from a file

printf 'X-One: 1\nX-Two: 2\n' > headers.txt
curl -s -H @headers.txt https://httpbin.org/headers

With @ and a file name, -H adds one header for each line of the file. Both X-One and X-Two arrived. This keeps tokens out of your shell history.

How do you show the response headers?

curl offers four ways, and they do not send the same request.

Terminal capture of the response header tests, 10 October 2026. curl -sI and curl -s -D - -o /dev/null both print the headers of example.com: HTTP/2 200, content-type text/html, server cloudflare and eight more. -w '%header{content-type}' prints text/html; charset=utf-8. -w '%{header_json}' piped to jq prints the server, content-type and cf-cache-status values as JSON arrays. curl -X HEAD over --http1.1 prints 200 and then exit 28. The same over HTTP/2 prints 200 and exit 0.
Our own run on our server, 10 October 2026, 19:52 UTC: curl 8.5.0 on Ubuntu 24.04, captured off-screen.
What you wantCommandWhat curl sends
The headers alonecurl -I URLA HEAD request.
The headers of a normal GETcurl -s -D - -o /dev/null URLA GET, and the body is thrown away.
Headers and body togethercurl -i URLA GET.
One header, or all as JSONcurl -s -o /dev/null -w '%header{content-type}' URLA GET.

-I sends HEAD, not GET

curl -sI https://example.com/
HTTP/2 200
date: Sat, 10 Oct 2026 19:51:34 GMT
content-type: text/html; charset=utf-8
server: cloudflare
last-modified: Fri, 09 Oct 2026 20:18:28 GMT
allow: GET, HEAD
accept-ranges: bytes
age: 517
cf-cache-status: HIT
cf-ray: a48829193bf24d23-ORD
alt-svc: h3=":443"; ma=86400

RFC 9110 defines HEAD as GET without the content, so the headers should match. Servers do not always follow that: a Stack Overflow question from 2014 asks why a HEAD request returned 404 while the full request returned 200. When the headers of the real GET matter, use -D - -o /dev/null. On example.com both ways printed the same status line and the same ten headers.

-i prints the headers and the body

-i puts the headers in front of the body. It was called --include before curl 8.10.0, and the old name still works.

-w picks one header, or all of them

curl -s -o /dev/null -w '%header{content-type}\n' https://example.com/
curl -s -o /dev/null -w '%{header_json}\n' https://example.com/

The first printed text/html; charset=utf-8. The second prints every response header as JSON, with each value in a list, such as "server":["cloudflare"]. %header{} needs curl 7.84.0 and header_json needs 7.83.0, so check older builds first.

-X HEAD is not the same as -I

The manual warns that -X HEAD does not make a proper HEAD request. It only changes the method word, and curl still waits for the body that the headers announce. We tried it with a 5-second limit:

CommandStatusExit code
curl -s --http1.1 -X HEAD -m 5 https://example.com/20028. curl waited until the limit.
curl -s -X HEAD -m 5 https://example.com/ (HTTP/2)2000. curl stopped at once.

Over HTTP/2 the server marks the end of the stream, so curl stopped. Over HTTP/1.1 it waited for bytes that never came. -I avoids the problem on both.

How do you see the headers curl sent?

-v prints the whole exchange. Lines that start with > are headers curl sent, and lines with < are headers it received. Over HTTP/1.1:

$ curl -sv --http1.1 -o /dev/null https://example.com/ 2>&1 | grep '^> '
> GET / HTTP/1.1
> Host: example.com
> User-Agent: curl/8.5.0
> Accept: */*

Over HTTP/2, curl lists the request differently:

* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: example.com]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.5.0]
* [HTTP/2] [1] [accept: */*]

The names are lowercase, because RFC 9113 requires lowercase field names in HTTP/2. Host became the :authority pseudo-header. A service that insists on a certain capitalization will see lowercase names over HTTP/2 either way.

Which headers reach the proxy, and which the site?

-H is for the site, and --proxy-header is for an HTTP proxy. We sent one of each through a stub proxy on our server that asked for the login USER:PASS and logged what it received.

Terminal capture of the proxy and redirect tests, 10 October 2026. curl -v shows the HTTP/1.1 request headers, then the HTTP/2 request as lowercase lines with :method, :scheme, :authority and :path. Through a stub proxy with an https:// address, httpbin sees X-Site but not X-Note, and the proxy log shows X-Note in the CONNECT request. With an http:// address, httpbin sees both headers, and the proxy log shows both. A redirect from httpbin to an echo server on 127.0.0.1 delivers X-Site without Authorization. With --location-trusted, Authorization arrives too.
Our own run on our server, 10 October 2026, 19:52 UTC: curl 8.5.0 on Ubuntu 24.04, a stub proxy and an echo server we wrote, both listening on 127.0.0.1.
curl -s -x http://127.0.0.1:18081 -U USER:PASS --proxy-header 'X-Note: for-proxy' -H 'X-Site: for-site' https://httpbin.org/headers
Target addressThe site receivedThe proxy received
https://httpbin.org/headersX-Site only.CONNECT httpbin.org:443 with X-Note and the login.
http://httpbin.org/headersX-Site and X-Note.GET http://httpbin.org/headers with both headers and the login.

For an https:// address, curl asks the proxy for a tunnel with CONNECT and puts --proxy-header headers in that request only. The site's own headers travel inside the encrypted tunnel. For a plain http:// address there is a single request, which the proxy passes on. RFC 9110 says a proxy must forward header fields it does not recognize, so the site received X-Note too. Keep secrets meant for a proxy away from plain HTTP.

The proxy login itself travels as a Proxy-Authorization header, which curl builds from -U. Our guide to using proxies with curl covers the flags in full, and 407 Proxy Authentication Required covers a refused login. With a real gateway the flags stay the same: -x http://premium.hproxy.com:10000 -U USER:PASS. The login comes from your dashboard, as on our residential proxies.

Which headers survive a redirect?

With -L, curl sends your -H headers to every address it follows, even on another host. The manual makes one exception: Authorization and Cookie set with -H stay behind when the redirect leaves the original host. We had httpbin.org redirect curl to the echo server on our own machine:

curl -sL -H 'Authorization: Bearer PLACEHOLDER' -H 'X-Site: for-site' 'https://httpbin.org/redirect-to?url=http://127.0.0.1:18090/echo'
echo server on 127.0.0.1 received:
  Host: 127.0.0.1:18090
  User-Agent: curl/8.5.0
  Accept: */*
  X-Site: for-site

X-Site followed the redirect, and Authorization did not. With --location-trusted added, the echo server received Authorization: Bearer PLACEHOLDER as well. Use that option only when you trust every host in the chain. Custom headers such as X-Api-Key get no such protection, so keep secrets out of them when a redirect might leave the site. Following redirects with curl covers logins given with -u and cookies.

What goes wrong most often?

What you seeWhyWhat to do
A help text that ends in Invalid category provided-h (lower case) is help, not headerUse -H.
Cannot bind parameter 'Headers'Windows PowerShell 5.1 ran Invoke-WebRequestType curl.exe, as What is cURL? shows.
A header that never arrives, exit code 0The command was copied with curly quotes ‘ ’Retype the quotes as plain ' or ".
The proxy header reached the sitePlain http:// through a proxy is one requestUse an https:// address, or keep secrets out of proxy headers.
Authorization missing after -Lcurl drops it on a redirect to another hostAdd --location-trusted, only for hosts you trust.
curl -X HEAD hangs, then exit 28-X changes the method word onlyUse -I.
Headers differ from what the page gets-I sent HEAD, the page uses GETUse -D - -o /dev/null.

The curly quotes case is quiet. In our run the shell split ‘X-Test: 1’ in two, curl treated 1’ as a second address and failed it with exit 6. It never sent X-Test and still finished with exit code 0.

What this page could not check

We ran one curl build, 8.5.0 on Ubuntu 24.04, and newer behaviour comes from the manual. Our response headers came from example.com and httpbin.org, which both sit behind large front ends that add or change headers. The proxy was our own stub, which forwards every header it does not know; a real proxy may strip or add some. We did not find a server that answers HEAD and GET differently on the day. That case rests on RFC 9110 and a Stack Overflow report. The -X HEAD hang was one server and one run each way. curl changes option names and -w variables between versions, so we will check this page again by 10 January 2027.

Sources

  • The curl man page, published by the curl project for version 8.23.0, read 10 October 2026: curl.se/docs/manpage.html.
  • RFC 9110, HTTP Semantics, June 2022, for field names, HEAD and how proxies forward fields: rfc-editor.org/rfc/rfc9110.
  • RFC 9113, HTTP/2, June 2022, for lowercase field names: rfc-editor.org/rfc/rfc9113.
  • Microsoft Learn, Invoke-WebRequest for Windows PowerShell 5.1 (its aliases curl and wget), updated 20 January 2026: learn.microsoft.com.
  • Amazon Web Services, request tracing for Application Load Balancers, read 10 October 2026: docs.aws.amazon.com.
  • Stack Overflow, "Curl header request returning 404 but body returning 200", asked 13 June 2014, read through the Stack Exchange API on 10 October 2026.
  • Our own runs of 10 October 2026, with curl 8.5.0 on Ubuntu 24.04. They went to httpbin.org and example.com, through a stub proxy and to an echo server we wrote. The research folder for this page holds every transcript, capture and script.

Frequently asked questions

How do I send a header with curl?
Add -H with the name and the value, for example curl -H 'X-Api-Key: 123' https://example.com/. Repeat -H for each header, or put one header per line in a file and pass -H @headers.txt. A header with the name of one curl sets itself, such as User-Agent, replaces curl's own.
How do I see the response headers with curl?
Use -I for the headers alone, which sends a HEAD request. Use -i for the headers and the body together. To see the headers of a normal GET without the body, use -D - -o /dev/null. For one header, use -w '%header{content-type}', available from curl 7.84.0.
How do I remove a header that curl sends by default?
Write the name with a colon and nothing after it, for example -H 'User-Agent:'. curl then leaves the header out. To send a header with an empty value instead, end the name with a semicolon: -H 'X-Empty;'.
How do I see the request headers curl sends?
Run curl -v and look at the lines that start with >. Over HTTP/2, curl lists the request as lines such as [HTTP/2] [1] [user-agent: curl/8.5.0], with lowercase names and the pseudo-headers :method, :scheme, :authority and :path.
Why does curl -X HEAD hang?
-X only changes the word in the request line. curl still waits for the body the response headers announce, and a HEAD response has none. Over HTTP/1.1 our test hung until its 5-second limit and exited with code 28. Use -I for a HEAD request.
Does curl send my headers again after a redirect?
Yes. Headers set with -H go to every host curl follows with -L. Authorization and Cookie headers set with -H are the exception: curl drops them when the redirect goes to another host, unless you add --location-trusted. Our test showed both.
How do I send a header only to the proxy?
Use --proxy-header instead of -H. For an https:// address, curl puts it only in the CONNECT request to the proxy. For a plain http:// address there is one request, and in our test the site received the proxy header as well.

Get proxies that are alive right now

Our free proxy list re-checks every exit every few minutes across 100+ countries, with a live last-checked time, so you copy IPs that worked moments ago, not a stale text dump. When the location has to survive a real check, the paid network holds up.

129M+ proxy checks run · 100+ countries · HTTP / HTTPS / SOCKS · re-checked every few minutes · no signup

HProxy.

Honest guides and comparisons on proxies, scraping and staying unblocked, from the team that runs the network.

RSS feed