Skip to content

cURL user agent: set, change and rotate the User-Agent header

Set curl's User-Agent with -A or -H, remove it, rotate it. Of 100 top sites, 8 treated curl's default differently. The TLS fingerprint never changed.

HProxy TeamOctober 11, 2026Updated October 11, 20269 min read

cURL user agent: set, change and rotate the User-Agent header

curl names itself in every request. Without an option it sends User-Agent: curl/8.5.0, or whatever version you run. -A changes that string, -H does too, and either one can remove it.

We tested every way of setting it with curl 8.5.0 on 11 October 2026. Then we asked 100 of the most visited sites for their home page twice: once with curl's own User-Agent and once with a Chrome one. Eight treated curl's string differently every time. The new string did not change curl's TLS fingerprint at all.

What User-Agent does curl send?

curl/ and its version number. The manual puts it plainly: "By default, curl uses curl/VERSION". To see it, run curl with -v and look for the request line that starts with >:

$ curl -v https://httpbin.org/user-agent 2>&1 | grep -i "^> user-agent"
> User-Agent: curl/8.5.0

Or ask a site that echoes it back. httpbin.org answered {"user-agent": "curl/8.5.0"}.

MDN describes the header as "a characteristic string that lets servers and network peers identify the application, operating system, vendor, and/or version of the requesting user agent". Sites read it for a reason. The HTTP standard, RFC 9110, says servers use it "to work around or tailor responses to avoid particular user agent limitations, and for analytics regarding browser or operating system use".

How do you set the User-Agent in curl?

With -A, short for --user-agent:

curl -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36" https://httpbin.org/headers

Or with -H, which sets any header: curl -H "User-Agent: my-scraper/1.0" https://httpbin.org/headers. For the site, both do the same, and cURL headers covers -H for every other header. We ran each form against httpbin.org, which echoes the headers it receives:

Terminal output: the User-Agent httpbin received with no option, with -A and a Chrome string, with -H, with -A and an empty string, with -H and no value, and with -A and a single space
Our own capture, 11 October 2026: ua_test.sh with curl 8.5.0 on our server

Put quotes around a string with spaces, or the shell splits it. Give the option twice and the last one counts. The manual: "If --user-agent is provided several times, the last set value is used." -A first/1.0 -A second/2.0 sent second/2.0.

How do you remove the User-Agent?

Give -A an empty string, as -A "". The manual says it then "removes the header completely from the request". -H "User-Agent:", with nothing after the colon, does the same. Both sent no User-Agent at all in our test.

-A " ", with one space, is different. It sends the header with an empty value. In the capture above, the brackets on the last line are empty: the header arrived with nothing in it.

Think twice before you remove it. RFC 9110 asks clients to send the header "unless specifically configured not to do so". Some sites insist on it. Wikimedia, the foundation behind Wikipedia, writes: "As of February 15, 2010, Wikimedia sites require a HTTP User-Agent header for all requests."

How do you use a variable or a config file?

Put double quotes around the variable. This is where scripts go wrong most often. We set UA="my-agent/1.0 (test)" and tried three ways:

$ curl -A "$UA" https://httpbin.org/user-agent
  { "user-agent": "my-agent/1.0 (test)"}
$ curl -A '$UA' https://httpbin.org/user-agent
  { "user-agent": "$UA"}
$ curl -A $UA https://httpbin.org/user-agent
  curl: (3) URL rejected: Bad hostname
  { "user-agent": "my-agent/1.0"}

Single quotes stop the shell from reading the variable, so the site got the text $UA. Without quotes, the shell split the value at the space. curl took my-agent/1.0 as the User-Agent and (test) as a second address, which it rejected. -H "User-Agent: $UA" worked just like -A "$UA".

To use one string every time, keep it in a config file. -K tells curl to "read curl arguments from" a text file:

$ cat ua.conf
  user-agent = "from-a-config-file/1.0"
$ curl -K ua.conf https://httpbin.org/user-agent
  { "user-agent": "from-a-config-file/1.0"}
$ curl -K ua.conf -A on-the-command-line/1.0 https://httpbin.org/user-agent
  { "user-agent": "on-the-command-line/1.0"}

An -A after the file still wins. A file named .curlrc in your home folder works without -K: curl "checks for a default config file and uses it if found". To skip it for one run, put -q first. Then "the curlrc config file is not read or used".

Does a new User-Agent stop blocks?

On most big sites, the string made no difference. We took the 100 top sites of the Tranco research list that answered with a web page. We asked each home page twice with the same curl, so only the User-Agent differed: curl's own, then a Chrome string. Whenever the two answers differed, we asked again at least three times, so a passing error would not count.

How 100 top sites answered curl's default User-Agent, compared with a Chrome string
No lasting difference
92 sites
Another page every time
5 sites
Refused curl every time
3 sites
Most sites did not care. Two of the eight that did still answered 200.
Source: HProxy test of Tranco list 8PXYV on 11 October 2026: home pages, curl 8.5.0, on our server; every difference asked again at least three times · hproxy.comProxy.

Three sites refused curl's string every time: one with 403, one with 501, one with 404 and once 429. Five more answered with status 200 but sent something else:

  • One sent {"ok":true,"msg":"healthy"}, a 27-byte health check, instead of a 69 KB home page.
  • One sent a captcha page titled "Are you not a robot?" instead of a 500 KB page.
  • Three sent the same page, with the same title, at about a third of the size and with fewer links.

The Chrome string got status 200 from all eight. So check the body, not only the status code. A script that waits for 200 would have taken a captcha page and a health check for success.

The bigger page for Chrome is no accident. When a client poses as another one, RFC 9110 says, "recipients can assume that the user intentionally desires to see responses tailored for that identified user agent". Send a Chrome string and you get the page built for Chrome.

Does a browser User-Agent make curl look like a browser?

No. A site can read more than one header. Before any HTTP is sent, the TLS handshake opens with a message called the ClientHello, and that message differs from program to program. FoxIO, who designed the JA4 fingerprint, explain: "JA4 looks at the TLS Client Hello packet and builds a fingerprint of the client based on attributes within the packet." Our guide to JA3 and JA4 fingerprinting walks through the handshake.

We asked tls.peet.ws, a public echo, for curl's fingerprint with two User-Agents. Then we asked it for a real Chrome's, run in headless mode on the same server:

ClientUser-Agent it sentJA4 fingerprint
curl 8.5.0curl/8.5.0t13d3112h2_e8f1e7e78f70_b26ce05bbdd6
curl 8.5.0the Chrome 141 stringt13d3112h2_e8f1e7e78f70_b26ce05bbdd6
Chrome 143its own, HeadlessChrome/143t13d1517h2_8daaf6152771_dcad5a053991

The User-Agent changed. The fingerprint did not. The first part of a JA4 counts what the client offered. FoxIO's spec starts with a "2 character number of cipher suites", then counts the extensions the same way. curl offered 31 cipher suites and 12 extensions, Chrome 15 and 17.

Bot protection reads this. Cloudflare's documentation says JA3 and JA4 fingerprints "identify TLS clients based on how they initiate connections". To a site that reads the handshake, curl with a Chrome string is still curl.

Should you rotate User-Agents?

Rotation changes one header and nothing else. Four of the five guides on this topic that we read suggest rotating browser strings. We did that with a Chrome, a Firefox and a Safari string in a file, agents.txt. Our script printed two fields of each answer:

$ for i in 1 2 3 4 5 6; do curl -s -A "$(shuf -n 1 agents.txt)" https://tls.peet.ws/api/all; done
  User-Agent: Safari   JA4: t13d3112h2_e8f1e7e78f70_b26ce05bbdd6
  User-Agent: Firefox  JA4: t13d3112h2_e8f1e7e78f70_b26ce05bbdd6
  User-Agent: Firefox  JA4: t13d3112h2_e8f1e7e78f70_b26ce05bbdd6
  User-Agent: Safari   JA4: t13d3112h2_e8f1e7e78f70_b26ce05bbdd6
  User-Agent: Safari   JA4: t13d3112h2_e8f1e7e78f70_b26ce05bbdd6
  User-Agent: Safari   JA4: t13d3112h2_e8f1e7e78f70_b26ce05bbdd6

To the site, those are a Firefox and a Safari with one and the same handshake, and the handshake is curl's. The standard discourages the trick itself. Borrowing another product's name, RFC 9110 says, "circumvents the purpose of the field".

There is an honest route as well. Wikimedia's rule for scripts reads: "Scripts should use an informative User-Agent string with contact information, or they may be blocked without notice." A string such as my-scraper/1.0 (+https://example.com/contact) does that. If a site really needs a browser, use a real browser.

What does a proxy see?

The User-Agent goes to the proxy too. For an https address, curl first asks the proxy to open a tunnel with a CONNECT request, and that request carries a User-Agent. After that, the proxy only relays encrypted bytes. RFC 9110 calls this "blind forwarding of data". We ran six commands through a small logging proxy of our own:

Terminal output: six curl commands through our logging proxy, each with the User-Agent the site received and the one on the proxy's CONNECT or GET line
Our own capture, 11 October 2026: proxy_ua_test.sh with curl 8.5.0 on our server

Three rules came out of it:

  • -A changes both copies. The site and the proxy both got the Chrome string.
  • -H changes only the site's copy. The proxy still read curl/8.5.0 on the CONNECT line. The manual says why: "You need --proxy-header to send custom headers intended for an HTTP proxy."
  • --proxy-header changes only the proxy's copy. With --proxy-header "User-Agent:", the CONNECT line carried no User-Agent, and the site still got the Chrome string.

For an http:// address there is no tunnel. The proxy receives the whole request, User-Agent included, as the last command shows. Our residential proxies take curl's -x the same way as the test proxy. cURL basic auth shows how to send a proxy login with -U.

What this page could not check

  • One server in the United States, curl 8.5.0, one Chrome string, and the home pages of 100 sites. Deeper pages, logins and APIs may treat clients differently.
  • We compared status codes, sizes, titles, tags and text length, not what each page said.
  • Our test proxy only logs and relays. Other proxies may add or change headers. We did not test SOCKS proxies or proxies reached over https://.
  • We will run the tests again with the current curl by 11 January 2027.

Sources

  • curl, man page, -A, -H, --proxy-header, -K and -q, read 11 October 2026: curl.se.
  • RFC 9110, HTTP Semantics, User-Agent and CONNECT, IETF, June 2022: rfc-editor.org.
  • MDN Web Docs, User-Agent, read 11 October 2026: developer.mozilla.org.
  • FoxIO, JA4 technical details, read 11 October 2026: github.com.
  • Cloudflare, JA3/JA4 fingerprint, page modified 6 May 2026: developers.cloudflare.com.
  • Wikimedia Foundation, User-Agent policy, read 11 October 2026: foundation.wikimedia.org.
  • Tranco, list 8PXYV, created 10 October 2026: tranco-list.eu.
  • Our own tests on our server, 11 October 2026: ua_test.sh at 09:09 UTC; ua_survey.py at 09:10 UTC, with repeats until 15:27 UTC; proxy_ua_test.sh at 15:19 UTC; ua_more.sh, ja4_test.sh and chrome_ja4.mjs from 15:29 UTC; rotate.sh at 15:37 UTC.

Frequently asked questions

What User-Agent does curl send by default?

curl/ followed by its version. Ours sent curl/8.5.0. Run curl -v and look for the line that starts with > User-Agent, or ask an echo such as httpbin.org/user-agent.

How do I change the User-Agent in curl?

Use -A with your string, or -H with User-Agent: and your string. Both set the same header for the site. If you give -A twice, the last one wins.

How do I remove the User-Agent header?

Give -A an empty string, or use -H with User-Agent: and nothing after the colon. Both removed the header in our test. -A with a single space sends the header with an empty value instead.

Does a browser User-Agent stop curl from being blocked?

Sometimes. Of 100 top sites, 8 treated curl's default differently every time, and all 8 answered a Chrome string with status 200. But the string does not change curl's TLS fingerprint, which bot protection also reads.

Why does curl get a strange page with status 200?

Some sites answer curl's default with another page instead of an error. In our test, one sent a captcha page and one a 27-byte health check, both with status 200. Check the body, not only the status.

Does a proxy see my curl User-Agent?

Yes. For an https address, curl puts its User-Agent on the CONNECT request that opens the tunnel. -A changes it there too, -H changes only the site's copy, and --proxy-header only the proxy's.

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