curl is a free command line tool for moving data to and from a server with URLs. You type curl and an address, and it prints what the server sends back. The same engine, a library called libcurl, runs inside phones, cars, routers and countless programs. The curl project counts over twenty billion installations. Windows 10 and 11 and macOS include curl, and most Linux distributions offer it as a package.
We ran every command on this page on 10 October 2026. On Linux we used curl 8.5.0, the version Ubuntu 24.04 ships, on our own server. On Windows we used curl 8.21.0, the copy Windows 11 includes. The targets were example.com, httpbin.org, our own site and a small proxy we wrote for the test. The output below is what came back.
What does curl stand for, and who makes it?
The name is a play on Client for URLs, according to the project's FAQ. It can also be read as see URL, which the FAQ says also helped. A later reading, curl URL Request Library, was not the original idea. You say it with a k sound, so it rhymes with girl.
curl began as HttpGet 0.1, which Rafael Sagula released on 11 November 1996. Daniel Stenberg extended it for an IRC bot that gave currency rates from the web. After two renames, curl 4 came out on 20 March 1998. Stenberg still leads the project, and a community of contributors builds it with him.
curl and libcurl are free and open source. Their license is inspired by the MIT license but not identical, and it allows commercial use.
Is curl already installed on your computer?
Open a terminal and ask curl for its version. On Windows, open Command Prompt and type curl.exe, for a reason the next section explains.
curl --version # Linux and macOS
curl.exe --version # Windows
On our Ubuntu 24.04 server the answer had four lines. The first names curl and every library it uses. Release-Date is when this version came out. Protocols lists what this build can speak, and Features lists extras such as HTTP2 and HTTPS-proxy.
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.10
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.15
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd
If the shell says the command is not found, install curl from your system's packages. That is apt install curl on Ubuntu and Debian, and dnf install curl on Fedora. The everything curl book notes that most Linux distributions offer curl when it is not there by default. macOS has bundled curl since Mac OS X 10.1 in 2001.
Which curl do you have?
The builds we checked differ more than their version numbers suggest.
| Where | curl version | Released | What stood out |
|---|---|---|---|
| Ubuntu 24.04, our server | 8.5.0 | 6 December 2023, with security patches | HTTP2 present, 25 protocols |
| Windows 11 build 26300, our workstation | 8.21.0 | 24 June 2026 | No HTTP2, no SCP or SFTP |
| The curl project, latest release | 8.22.0 | 2 September 2026 | As listed on curl.se on 10 October 2026 |
| The online manual | 8.23.0 | Not released on 10 October 2026 | Describes options your build may not have |
curl normally releases every eight weeks, so the copy on your computer is usually older than the manual online. Our Ubuntu build dates from December 2023, although Ubuntu patches its security holes. Windows' own build lacks HTTP/2, and it refused the option before sending anything, with exit code 2:
> curl.exe --http2 -sS -o NUL https://example.com/
curl: option --http2: the installed libcurl version does not support this
curl: try 'curl --help' for more information
Why does curl behave differently in PowerShell?
In Windows PowerShell 5.1, the word curl does not start curl. Microsoft's reference lists curl as an alias for Invoke-WebRequest, a PowerShell command with its own options. A curl command pasted into it fails in odd ways. We ran two on Windows 11, with the English interface:
PS> curl -H "Accept: text/html" https://example.com
Cannot bind parameter 'Headers'. Cannot convert the "Accept: text/html" value of type "System.String" to type "System.Collections.IDictionary".
PS> curl -I https://example.com
Cannot process command because of one or more missing mandatory parameters: Uri.
Both failed while PowerShell read the parameters, before any request. The first error is the title of a Stack Overflow question from 2017 with over 180,000 views. The fix is to type curl.exe, which runs the real program. PowerShell 7 dropped the alias: Microsoft's reference for it lists only iwr, and PowerShell 7.6.6 on our machine had no curl alias at all.
How does the curl command work?
The manual gives the form as curl [options / URLs]. Anything that is not an option or an option's value is taken as a URL. With no options, curl sends a GET request for each URL and writes the answer to the terminal. It does not parse or change what it receives.
A few rules from the manual save time:
- Without a scheme such as
https://, curl guesses. It defaults to HTTP, and picks FTP for host names that start withftp.. - Options have a short and a long form:
-oand--outputare the same. Short options without a value can be joined, so-OLvmeans-O -L -v. - Several URLs on one line are fetched one after another, over one connection where possible.
- Put a URL in quotes when it holds
&,?,[ ]or{ }. Otherwise the shell, or curl's own globbing, changes it. - The exit code says whether the transfer worked. A later section shows how to read it.
curl had 273 command line options in November 2025, by the project's own count. The table further down lists the twenty this page uses.
Your first curl commands, step by step
You need a terminal with curl in it, which the section above checks. Step 5 also pipes the answer through jq, a separate JSON tool; leave that part out if you do not have it.
Each step shows the command we ran and the real output, from curl 8.5.0 on Ubuntu 24.04. RFC 2606 reserves example.com for use in examples. Its own page asks people to avoid relying on it for testing and monitoring, so keep to a few requests.

Step 1: fetch a page
curl https://example.com/
curl prints the HTML of the page: 577 bytes in our run, the same bytes step 3 saves to a file. It starts like this:
<!doctype html><html lang=en><head><meta charset=utf-8><link rel=icon href=data:,><meta name=viewport content="width=device-width,initial-scale=1"><title>Example Domain</title>
Step 2: look at the headers only
curl -sI https://example.com/
-I asks for the headers only, with a HEAD request, and -s keeps curl quiet:
HTTP/2 200
date: Sat, 10 Oct 2026 18:42:57 GMT
content-type: text/html; charset=utf-8
server: cloudflare
last-modified: Fri, 09 Oct 2026 20:19:47 GMT
allow: GET, HEAD
accept-ranges: bytes
age: 10800
cf-cache-status: HIT
cf-ray: a487c493ee142145-ORD
alt-svc: h3=":443"; ma=86400
The first line is the status: HTTP/2, code 200, which means OK. To see the headers and the page together, use -i. It was called --include before curl 8.10.0, and both names still work. Our guide to cURL headers covers sending and reading them.
Step 3: save the page to a file
curl -o page.html https://example.com/
With -o, the page goes into the file, and curl shows a progress meter on the screen:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 577 0 577 0 0 2717 0 --:--:-- --:--:-- --:--:-- 2721
That reads: 100 percent done, 577 bytes received, at an average of 2,717 bytes per second. -O saves under the file name from the URL instead. -s hides the meter. Downloading files with curl covers names, folders and resuming.
Step 4: follow a redirect
curl -sIL http://hproxy.com/ | grep -iE '^(HTTP|location)'
HTTP/1.1 301 Moved Permanently
Location: https://hproxy.com/
HTTP/2 200
Our own site sends plain http:// visitors to https:// with a 301. Without -L, curl stops at the 301 and shows you that answer. With -L, it makes the next request itself. It follows up to 50 redirects by default, and --max-redirs changes the limit. Following redirects with curl covers what happens to a POST or a login on the way.
Step 5: send data
curl -s --json '{"tool": "curl"}' https://httpbin.org/post | jq -c '{json, sent_as: .headers["Content-Type"]}'
{"json":{"tool":"curl"},"sent_as":"application/json"}
httpbin.org echoes what it receives, and jq picked out two fields. --json sends the text as the request body and sets both Content-Type and Accept to application/json. It needs curl 7.82.0 or newer. For form fields, use -d name=value, which curl sends as application/x-www-form-urlencoded. You rarely need -X POST: -d and --json already make a POST, and -X only changes the method word. The cURL POST guide covers forms, JSON and uploads.
Step 6: watch the whole conversation
curl -v -o /dev/null https://example.com/
With -v, curl prints each step as it happens. A line with > is a header curl sent, < is a header it received, and * is curl explaining itself. Some lines from our run:
* Connected to example.com (172.66.147.243) port 443
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / id-ecPublicKey
* SSL certificate verify ok.
> GET / HTTP/2
> Host: example.com
> User-Agent: curl/8.5.0
> Accept: */*
< HTTP/2 200
< content-type: text/html; charset=utf-8
The User-Agent line shows how curl introduces itself by default: curl, a slash and its version. -A sets another name. The verify ok line matters too: curl has checked TLS certificates by default since version 7.10.
How do you know a curl command worked?
curl reports success through its exit code, not through the page. Code 0 means the transfer finished, and any other number names the step that failed. Read it with echo $? in bash or $LASTEXITCODE in PowerShell.

The trap is an error page. By default, curl does not count HTTP error codes as failures, so a 404 still exits 0:
$ curl -s -o /dev/null -w '%{http_code}\n' https://httpbin.org/status/404
404
[exit 0]
$ curl -sS -f -o /dev/null https://httpbin.org/status/404
curl: (22) The requested URL returned error: 404
[exit 22]
Put -f (--fail) in every script, so an answer of 400 or more becomes exit 22. Use -w '%{http_code}' when you want the status itself. -sS hides the progress meter but keeps error messages.
The exit codes below come from curl's manual, which lists 0 to 100 and promises never to change the existing ones. The last column says where we saw each code on 10 October 2026.
| Exit code | Meaning in the manual | Where we saw it |
|---|---|---|
| 0 | Success | Every working command, and the 404 without -f |
| 5 | Could not resolve proxy | -x POST typed for -X POST |
| 6 | Could not resolve host | example.invalid, a name RFC 2606 reserves as invalid |
| 7 | Failed to connect to host | Not run here |
| 22 | HTTP page not retrieved (only with --fail) | The 404 with -f |
| 28 | Operation timeout | Not run here |
| 35 | SSL connect error | Not run here |
| 47 | Too many redirects | Not run here |
| 52 | The server did not reply anything | Not run here |
| 56 | Failure in receiving network data | A proxy that wanted a login, response 407 |
| 60 | Peer certificate cannot be authenticated | An HTTPS proxy with a self-signed certificate |
| 97 | Proxy handshake error | Not run here |
In our run, exit 5 came from a typo. -x (lower case) names a proxy and -X (upper case) names a method. Typing -x POST makes curl look for a proxy called POST, which our guide to curl errors 5 and 97 takes apart.
Which curl options will you use most?
Every row below is quoted or summed up from the manual. A dash in the first column means the option has only a long form.
| Option | Long form | What it does |
|---|---|---|
-o file | --output | Writes the body to a file instead of the terminal |
-O | --remote-name | Saves under the file name from the URL |
-L | --location | Follows redirects |
-I | --head | Fetches the headers only, with HEAD |
-i | --show-headers | Prints the headers before the body |
-H "Name: value" | --header | Adds or replaces a request header |
-d data | --data | Sends data as a form POST |
| - | --json data | Sends JSON as a POST, from 7.82.0 |
-X METHOD | --request | Changes the method word, such as PUT or DELETE |
-A name | --user-agent | Sets the User-Agent header |
-u user:pass | --user | Logs in to the server |
-v | --verbose | Shows the whole conversation |
-s and -S | --silent, --show-error | Hides the meter and messages, then shows errors again |
-f | --fail | Exits 22 on an HTTP answer of 400 or more |
-w format | --write-out | Prints details such as %{http_code} afterwards |
-m seconds | --max-time | Limits the whole transfer |
| - | --connect-timeout | Limits the connection phase |
-x proxy | --proxy | Sends the request through a proxy |
-U user:pass | --proxy-user | Logs in to the proxy |
-k | --insecure | Skips the certificate check, which makes the transfer insecure |
Newer builds add options yours may lack. --follow and --out-null arrived in curl 8.16.0, so our Ubuntu build does not know them. Run curl --help all to list what your own build supports.
How do proxies fit into curl?
A proxy makes the request for you, so the target sees the proxy's address instead of yours. curl takes a proxy with -x (--proxy) and its login with -U (--proxy-user):
curl -x http://premium.hproxy.com:10000 -U USER:PASS https://example.com/
The manual sets four rules worth knowing:
- No scheme, or
http://, means an HTTP proxy.https://means curl speaks TLS to the proxy itself.socks4://,socks4a://,socks5://andsocks5h://pick a SOCKS version. - Without a port, curl assumes 1080. Always write the port.
- curl also reads proxies from environment variables such as
HTTPS_PROXYandALL_PROXY. The variable for plainhttp://sites works only in lower case, ashttp_proxy. --noproxylists hosts that skip the proxy. A single*is its only wildcard, and it matches every host.
We tested these against a stub proxy we wrote, on our own server. It listened on 127.0.0.1, asked for the login USER:PASS, and logged what curl sent it. The second capture above shows the full run.
| What we ran | Exit code | What the proxy logged |
|---|---|---|
-x http://127.0.0.1:18081, no login | 56: CONNECT tunnel failed, response 407 | CONNECT example.com:443, answered 407 |
The same with -U USER:PASS | 0, page status 200 | CONNECT example.com:443 with Proxy-Authorization: Basic VVNFUjpQQVNT |
-x socks5://USER:PASS@127.0.0.1:11081 | 0, page status 200 | An IPv4 address that curl had looked up |
-x socks5h://USER:PASS@127.0.0.1:11081 | 0, page status 200 | The name example.com |
-x https://localhost:18444, self-signed certificate | 60: SSL certificate problem: self-signed certificate | Nothing |
The same with --proxy-insecure | 0, page status 200 | CONNECT example.com:443 |
--noproxy '' with the HTTP proxy | 0, page status 200 | The request, as before |
--noproxy '*' with the HTTP proxy | 0, page status 200 | Nothing: curl went direct |
Four lessons come out of that table:
VVNFUjpQQVNTis USER:PASS in Base64, which anyone can decode. RFC 7617 calls Basic authentication not secure unless something like TLS protects it. With anhttp://proxy, that header reaches the proxy unencrypted.- With
socks5://, curl looks up the name and hands the proxy an address. Withsocks5h://, the proxy gets the name and looks it up itself. Usesocks5hwhen your own DNS should not see the names you visit. --proxy-insecureskips the certificate check for the proxy only.-kskips it for the target, the site you care about. They are not interchangeable.--noproxy ''does not switch the proxy off. The manual says an empty list overrides an exception list from the environment, and only*sends every request direct. Some guides claim the opposite, but our stub logged the request.
The full reference, with environment variables and proxy lists, is how to use proxies with curl. When a proxy command fails, the cURL proxy error guide decodes exits 7, 56 and 60. A stub shows what curl sends, not what a website sees from a real exit address. For that, our residential proxies take the same -x and -U flags, with the host, port and login from your dashboard.
What is curl not?
curl is not a browser. It runs no JavaScript and loads nothing the page points to. The example.com page we saved carries a script tag, <script src=/s.js>, yet curl made one request and kept 577 bytes. A page that builds its content with scripts reaches you as the raw HTML, before any script runs.
curl is not wget either. The project's FAQ says curl is not a Wget clone and not a website mirroring program. It is made for single transfers, and you script around it for more. curl-impersonate is a third tool again: a curl build that copies a real browser's TLS and HTTP handshakes.
curl is not the PHP extension, although they share an engine. PHP's curl functions call libcurl, and PHP has offered that module since version 4.0.2. The Stack Overflow question "What is cURL in PHP?" has nearly 495,000 views.
What goes wrong most often?
| What you see | Why | What to do |
|---|---|---|
curl: command not found | curl is not installed, or not on the path | Install it from your system's packages |
Cannot bind parameter 'Headers' in PowerShell | Windows PowerShell 5.1 ran Invoke-WebRequest | Type curl.exe |
curl: (5) Could not resolve proxy: POST | -x typed for -X | Use -X POST, or leave it out with -d |
| A 404 page, and exit code 0 | curl does not treat HTTP errors as failures | Add -f, or print %{http_code} |
A URL cut off at & | The shell read the & | Put the URL in quotes |
option --http2: the installed libcurl version does not support this | Your build lacks the feature | Check curl --version, or use another build |
curl: (60) SSL certificate problem | The certificate is not trusted | Fix the trust, not the check; the SSL guide shows how, and why -k is the wrong fix |
What this page could not check
We did not run macOS, Windows 10 or other Linux distributions, so their bundled curl versions are not in our table. Our Linux runs used one build, curl 8.5.0 on Ubuntu 24.04. Options added after it, such as --follow and --out-null in 8.16.0, come from the manual, not from a run. The proxy tests used a stub on our own server, so they show what curl sends, not how a commercial proxy answers. The twenty billion installations figure is the curl project's own estimate. Microsoft dates curl in Windows to Insider build 17063, while curl's history names the 1803 release of Windows 10. curl releases about every eight weeks and Windows updates its copy, so we will check the versions on this page again by 10 January 2027.
Sources
- curl project, home page, read 10 October 2026: curl.se.
- curl project, the curl man page (describes 8.23.0), read 10 October 2026: curl.se/docs/manpage.html.
- curl project, FAQ, read 10 October 2026: curl.se/docs/faq.html.
- curl project, history of the project, read 10 October 2026: curl.se/docs/history.html.
- curl project, copyright and license, read 10 October 2026: curl.se/docs/copyright.html.
- curl project, release procedure, read 10 October 2026: curl.se/dev/release-procedure.html.
- everything curl, the name, and installing on Linux and macOS, read 10 October 2026: everything.curl.dev.
- Microsoft, "Tar and Curl Come to Windows!", 18 January 2018: devblogs.microsoft.com.
- Microsoft Learn, Invoke-WebRequest (PowerShell 5.1 and 7.5), updated 20 January 2026: learn.microsoft.com.
- Microsoft Learn, about_Automatic_Variables ($LASTEXITCODE), updated 2 April 2026: learn.microsoft.com.
- GNU Bash manual, Special Parameters ($?), read 10 October 2026: gnu.org.
- curl-impersonate, README of the maintained fork, read 10 October 2026: github.com/lexiforest/curl-impersonate.
- RFC 2606, Reserved Top Level DNS Names, June 1999: rfc-editor.org/rfc/rfc2606.
- RFC 9110, HTTP Semantics, June 2022, for the status codes 200, 301, 404 and 407 and what a proxy is: rfc-editor.org/rfc/rfc9110.
- RFC 7617, The Basic HTTP Authentication Scheme, September 2015: rfc-editor.org/rfc/rfc7617.
- HProxy, API documentation for plans and sticky sessions, for the gateway form premium.hproxy.com:10000: /docs/proxy-api/plans.
- Stack Overflow question counts, read through the Stack Exchange API on 10 October 2026.
- Our own measurement: the commands on this page, run on 10 October 2026. We used curl 8.5.0 on Ubuntu 24.04, curl 8.21.0 on Windows 11 and a stub proxy we wrote. The transcripts, captures and scripts are in the research folder for this page.


