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:

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.
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:
| Client | User-Agent it sent | JA4 fingerprint |
|---|---|---|
| curl 8.5.0 | curl/8.5.0 | t13d3112h2_e8f1e7e78f70_b26ce05bbdd6 |
| curl 8.5.0 | the Chrome 141 string | t13d3112h2_e8f1e7e78f70_b26ce05bbdd6 |
| Chrome 143 | its own, HeadlessChrome/143 | t13d1517h2_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:

Three rules came out of it:
-Achanges both copies. The site and the proxy both got the Chrome string.-Hchanges only the site's copy. The proxy still readcurl/8.5.0on the CONNECT line. The manual says why: "You need --proxy-header to send custom headers intended for an HTTP proxy."--proxy-headerchanges 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.




