Tutorial

What is cURL? The curl command, its options and how proxies fit in

curl is a command line tool for moving data with URLs. We ran its basic commands, exit codes and proxy flags on 10 October 2026 and show what came back.

HProxy Team··Updated October 10, 2026·18 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→

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.

Wherecurl versionReleasedWhat stood out
Ubuntu 24.04, our server8.5.06 December 2023, with security patchesHTTP2 present, 25 protocols
Windows 11 build 26300, our workstation8.21.024 June 2026No HTTP2, no SCP or SFTP
The curl project, latest release8.22.02 September 2026As listed on curl.se on 10 October 2026
The online manual8.23.0Not released on 10 October 2026Describes 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 with ftp..
  • Options have a short and a long form: -o and --output are the same. Short options without a value can be joined, so -OLv means -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.

Our terminal on 10 October 2026. curl --version reports curl 8.5.0 from 2023-12-06. curl -sI on example.com returns HTTP/2 200 from Cloudflare. curl -o shows the progress meter for 577 bytes, and the saved title is Example Domain. On http://hproxy.com/, curl -sIL shows a 301 to https://hproxy.com/ and then HTTP/2 200. curl --json to httpbin.org echoes the JSON as application/json. Every command ends with exit 0.
Our own run on our server, 10 October 2026, 18:42 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: 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.

Our terminal on 10 October 2026. example.invalid gives exit 6. A 404 from httpbin exits 0, and exits 22 with -f. curl -x POST gives exit 5, Could not resolve proxy: POST. Then a stub proxy on 127.0.0.1. Without a login, curl exits 56 with response 407. With -U USER:PASS, the proxy logs Proxy-Authorization: Basic VVNFUjpQQVNT. socks5 hands the proxy an IPv4 address, and socks5h hands it the name. An HTTPS proxy with a self-signed certificate fails with exit 60 until --proxy-insecure. With --noproxy '' the request still goes through the proxy. With --noproxy '*' it goes direct.
Our own run on our server, 10 October 2026, 18:43 UTC: curl 8.5.0 on Ubuntu 24.04 and a stub proxy we wrote, listening on 127.0.0.1 with the login USER:PASS. Under each command, the lines the proxy logged and the exit code.

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 codeMeaning in the manualWhere we saw it
0SuccessEvery working command, and the 404 without -f
5Could not resolve proxy-x POST typed for -X POST
6Could not resolve hostexample.invalid, a name RFC 2606 reserves as invalid
7Failed to connect to hostNot run here
22HTTP page not retrieved (only with --fail)The 404 with -f
28Operation timeoutNot run here
35SSL connect errorNot run here
47Too many redirectsNot run here
52The server did not reply anythingNot run here
56Failure in receiving network dataA proxy that wanted a login, response 407
60Peer certificate cannot be authenticatedAn HTTPS proxy with a self-signed certificate
97Proxy handshake errorNot 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.

OptionLong formWhat it does
-o file--outputWrites the body to a file instead of the terminal
-O--remote-nameSaves under the file name from the URL
-L--locationFollows redirects
-I--headFetches the headers only, with HEAD
-i--show-headersPrints the headers before the body
-H "Name: value"--headerAdds or replaces a request header
-d data--dataSends data as a form POST
---json dataSends JSON as a POST, from 7.82.0
-X METHOD--requestChanges the method word, such as PUT or DELETE
-A name--user-agentSets the User-Agent header
-u user:pass--userLogs in to the server
-v--verboseShows the whole conversation
-s and -S--silent, --show-errorHides the meter and messages, then shows errors again
-f--failExits 22 on an HTTP answer of 400 or more
-w format--write-outPrints details such as %{http_code} afterwards
-m seconds--max-timeLimits the whole transfer
---connect-timeoutLimits the connection phase
-x proxy--proxySends the request through a proxy
-U user:pass--proxy-userLogs in to the proxy
-k--insecureSkips 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:// and socks5h:// pick a SOCKS version.
  • Without a port, curl assumes 1080. Always write the port.
  • curl also reads proxies from environment variables such as HTTPS_PROXY and ALL_PROXY. The variable for plain http:// sites works only in lower case, as http_proxy.
  • --noproxy lists 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 ranExit codeWhat the proxy logged
-x http://127.0.0.1:18081, no login56: CONNECT tunnel failed, response 407CONNECT example.com:443, answered 407
The same with -U USER:PASS0, page status 200CONNECT example.com:443 with Proxy-Authorization: Basic VVNFUjpQQVNT
-x socks5://USER:PASS@127.0.0.1:110810, page status 200An IPv4 address that curl had looked up
-x socks5h://USER:PASS@127.0.0.1:110810, page status 200The name example.com
-x https://localhost:18444, self-signed certificate60: SSL certificate problem: self-signed certificateNothing
The same with --proxy-insecure0, page status 200CONNECT example.com:443
--noproxy '' with the HTTP proxy0, page status 200The request, as before
--noproxy '*' with the HTTP proxy0, page status 200Nothing: curl went direct

Four lessons come out of that table:

  1. VVNFUjpQQVNT is USER:PASS in Base64, which anyone can decode. RFC 7617 calls Basic authentication not secure unless something like TLS protects it. With an http:// proxy, that header reaches the proxy unencrypted.
  2. With socks5://, curl looks up the name and hands the proxy an address. With socks5h://, the proxy gets the name and looks it up itself. Use socks5h when your own DNS should not see the names you visit.
  3. --proxy-insecure skips the certificate check for the proxy only. -k skips it for the target, the site you care about. They are not interchangeable.
  4. --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 seeWhyWhat to do
curl: command not foundcurl is not installed, or not on the pathInstall it from your system's packages
Cannot bind parameter 'Headers' in PowerShellWindows PowerShell 5.1 ran Invoke-WebRequestType curl.exe
curl: (5) Could not resolve proxy: POST-x typed for -XUse -X POST, or leave it out with -d
A 404 page, and exit code 0curl does not treat HTTP errors as failuresAdd -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 thisYour build lacks the featureCheck curl --version, or use another build
curl: (60) SSL certificate problemThe certificate is not trustedFix 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

Frequently asked questions

What does curl stand for?
The curl project says the name is a play on Client for URLs, and that it can also be read as see URL. A later reading, curl URL Request Library, was not the original idea. You say it with a k sound, so it rhymes with girl.
Is curl installed on Windows?
Yes, on Windows 11 and on Windows 10 since 2018. Microsoft added curl.exe with Insider build 17063, announced in January 2018. On our Windows 11 machine the bundled copy was curl 8.21.0. Open Command Prompt and type curl --version to check. In Windows PowerShell 5.1, type curl.exe instead.
Why does curl give strange errors in PowerShell?
In Windows PowerShell 5.1, curl is an alias for Invoke-WebRequest, a different command with different options. A header passed with -H fails with Cannot bind parameter 'Headers'. Type curl.exe to run the real curl, or use PowerShell 7, which no longer has the alias.
What is the difference between curl and libcurl?
curl is the command you type. libcurl is the library that does the transfers, and curl is built on it. Other programs use libcurl directly: PHP's curl functions are a binding to it, and the project counts over twenty billion installations.
Is curl the same as wget?
No. The curl project says curl is not a Wget clone and not a website mirroring program. It is made for single transfers to or from a URL, in many protocols. To fetch a whole site with curl, you write a script around it.
How do I know if a curl command worked?
Read its exit code: 0 means the transfer finished. A 404 page still exits 0, because curl does not treat HTTP error codes as failures. Add -f to turn them into exit 22, or print the status with -w '%{http_code}'.
How do I use a proxy with curl?
Pass the proxy with -x and its login with -U, for example curl -x http://premium.hproxy.com:10000 -U USER:PASS https://example.com/. Always write the port, because curl assumes 1080 when it is missing. Use socks5h:// for a SOCKS5 proxy that should look up the names itself.

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