curl and Wget both fetch files from the command line, and they differ from the first command. curl prints the answer and does only what you ask. Wget saves files, follows redirects, picks up broken downloads and can copy a whole site. With proxies, curl does everything Wget does and adds SOCKS5.
We ran both side by side on 11 October 2026: curl 8.5.0 and GNU Wget 1.21.4, against httpbin.org, a public echo service, and small test servers of our own. Every result below comes from that run.
What is the difference between curl and Wget?
The short version comes from Daniel Stenberg, who wrote curl and also contributes to Wget: "curl works more like the traditional Unix cat command", while "Wget is more like cp". One sends data where you point it. The other makes files.
We ran each one without extra options:
| curl 8.5.0 | GNU Wget 1.21.4 | |
|---|---|---|
| The answer | printed to the terminal | saved as a file |
| A redirect | stops at it, -L follows | followed, up to 20 |
| A download that breaks | stops, exit code 18 | continues from the break |
| A 503 from the server | one request, exit code 0 | one request, exit code 8 |
| A 404 | saves the error page, exit code 0 | saves nothing, exit code 8 |
| HTTP version, to a site that offers HTTP/2 | HTTP/2 | HTTP/1.1 |
| User-Agent | curl/8.5.0 | Wget/1.21.4 |
| Copying a whole site | no | with -r |
| Many files at once | with -Z, 50 at a time | one after another |
| SOCKS5 proxies | yes | no |
| Uploads | the raw file or a multipart form | the file, sent as a form |
The sections below show each test.
Which one follows redirects?
Wget does, and curl does not until you add -L. On a page that redirected twice, curl stopped at the first 302, and Wget followed both. Wget's manual sets its limit: "The default is 20, which is usually far more than necessary." curl's -L makes it "redo the request to the new place".
This explains a common puzzle: curl -O saves a tiny file where Wget saves the real one. curl saves whatever the first answer was:
$ curl -O https://httpbin.org/redirect/1
exit code 0; saved:
1, 215 bytes, starting: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><title>Redirec
$ curl -L -O https://httpbin.org/redirect/1
exit code 0; saved:
1, 255 bytes, starting: { "args": {}, "headers": { "Accept": "*/*", "Host": "htt
$ wget https://httpbin.org/redirect/1
exit code 0; saved:
1, 292 bytes, starting: { "args": {}, "headers": { "Accept": "*/*", "Accept-Enco
The 215-byte file is httpbin's "Redirecting..." page, not the content. Add -L, and curl saves the same file Wget does.
What happens when a download breaks?
Wget picks it up on its own. curl stops. We built a small server that cuts the first download of a 20 MB file after 5 MB and answers normally after that. A fresh server ran for each case:

- Wget printed "Connection closed at byte 5242880. Retrying." and asked for the rest, from byte 5,242,880. The file came out complete, with the same SHA-256 checksum as the original.
- curl stopped with exit code 18 and a 5 MB file.
curl --retry 3did not retry. A broken transfer is not one of the errors it retries. The manual lists those: "a timeout, an FTP 4xx response code or an HTTP 408, 429, 500, 502, 503, 504, 522 or 524 response code".curl --retry-all-errorsretried, but downloaded the whole file again from byte 0, even with-C -. The manual warns that "before retrying it removes output data from a failed partial transfer that was written to an output file".
Daniel Stenberg's comparison says the same: Wget's "ability to recover from a prematurely broken transfer and continue downloading has no counterpart in curl". That holds for curl 8.5.0, released on 6 December 2023. curl's changelog for 8.17.0, released on 5 November 2025, lists "keep failed partial download for retry auto-resume", so newer versions may resume on a retry.
By hand, both resume. After a break, curl -C - -o file.bin URL and wget -c URL both asked the server for bytes=5242880- and finished the file. The server answers such a request with 206 Partial Content, which RFC 9110 describes as "successfully fulfilling a range request for the target resource".
Does either one retry server errors?
Not by default. A page that answered 503 got one request from each tool. Wget's tries are for network failures: "The default is to retry 20 times". Its manual calls 503 and 429 errors "normally not retried by Wget", and offers --retry-on-http-error for them.
curl retried the 503 with --retry 3: four requests in all, with waits of 1, 2 and 4 seconds. Its exit code stayed 0, though. Without -f, curl counts any answer from the server as a success. On a 404 it saved the error page and exited 0; with -f it exited 22 and saved nothing. Wget exited 8 on both. In a script, give curl -f, or check Wget's exit code.
Can curl download a whole site like Wget?
No. In Daniel Stenberg's words, curl "transfers just the URLs that the user specifies, and does not contain any recursive downloading logic nor any sort of HTML parser". Wget does. We built a five-page test site and ran wget -r -l 2 on it:
$ wget -r -l 2 http://127.0.0.1:PORT/
(Wget's progress lines left out)
exit code 0; files saved:
./127.0.0.1:PORT/a.html
./127.0.0.1:PORT/c.html
./127.0.0.1:PORT/img.png
./127.0.0.1:PORT/index.html
./127.0.0.1:PORT/robots.txt
./127.0.0.1:PORT/style.css
the server's log, in order:
GET / HTTP/1.1 -> 200 (User-Agent: Wget/1.21.4)
GET /robots.txt HTTP/1.1 -> 200 (User-Agent: Wget/1.21.4)
GET /style.css HTTP/1.1 -> 200 (User-Agent: Wget/1.21.4)
GET /a.html HTTP/1.1 -> 200 (User-Agent: Wget/1.21.4)
GET /img.png HTTP/1.1 -> 200 (User-Agent: Wget/1.21.4)
GET /c.html HTTP/1.1 -> 200 (User-Agent: Wget/1.21.4)
Wget read robots.txt right after the first page, and skipped b.html, which robots.txt disallows. Its manual says so: "Wget respects the Robot Exclusion Standard (/robots.txt)". It followed the stylesheet to the image, and it did not leave the host for the link to example.com. curl -O on the same site saved index.html and nothing else.
Which one is faster?
For one big file, neither. We downloaded Linode's public 100 MB test file ten times with each tool, taking turns. curl's median was 10.3 seconds and Wget's 8.0, but each tool's slowest run took about three times its fastest. The network decided, not the tool.
For many small files, curl's parallel mode wins. Daniel Stenberg: "curl can do many transfers in parallel (-Z). Wget only does serial." We served 1,000 files of 10 KiB from a test server on the same machine and timed five runs of each command:
Without -Z, both used a single connection for all 1,000 files, and curl's command was faster than Wget's list in all five rounds. With -Z, curl opened 50 connections and finished about nine times faster than Wget.
Which one works better with a proxy?
Both work with an ordinary HTTP proxy. We ran each through small logging proxies of our own:
- On the command line:
curl -x http://host:portandwget -e use_proxy=yes -e https_proxy=http://host:port. - From the environment: both read the
https_proxyvariable. - With a login:
curl -U user:pass, andwget --proxy-user=user --proxy-password=passor the login inside the proxy address. Both sent the login with their first request to the proxy.
Without the login, curl reported a 407 and Wget "Proxy tunneling failed". Each tool also showed the proxy its User-Agent, curl/8.5.0 and Wget/1.21.4. Our residential proxies work with either tool as an HTTP proxy with a login.
SOCKS5 is where they part:

curl worked both ways. With socks5h://, it handed the proxy the host name. With socks5://, it sent an address it had looked up itself. The SOCKS5 standard, RFC 1928, has its own address type for the first case: "the address field contains a fully-qualified domain name". Pick socks5h:// when the proxy should do the lookup: the manual says it lets "the proxy resolve the hostname".
Wget refused with "Unsupported scheme" and never contacted the proxy. Daniel Stenberg's comparison is blunt: "Wget has no SOCKS support." Our guide to proxies with wget shows the wrapper route.
What else is different?
- The TLS handshake. curl offered 31 cipher suites and listed h2 and http/1.1 in its ALPN extension, then spoke HTTP/2. Wget offered 75 cipher suites and no ALPN, and spoke HTTP/1.1. Their JA4 fingerprints,
t13d3112h2_e8f1e7e78f70_b26ce05bbdd6andt13d7511_479067518aa3_fb8d5ffd48c1, differ in every count and both hashes, so a site that reads them can tell the two apart. See cURL user agent for why a browser User-Agent does not change that. - Uploads.
curl -Tsent a file as it was, andcurl -Fsent it as a multipart form, the way a browser uploads. Wget sent it with--method=PUT --body-file, but labelled it a form, so the test server read the file as a form field. Its manual says Wget does not support multipart form data. - The letter -U. In Wget it sets the User-Agent. In curl it is the proxy login:
curl -U "my-agent/1.0"stopped and asked "Enter proxy password for user 'my-agent/1.0':". - Where they come from. "curl comes pre-installed on macOS and Windows 10/11. Wget does not." "curl is powered by libcurl", a library other programs build on, while "Wget is command line only." Their licenses differ too: "Wget is GPL v3. curl is MIT licensed."
Which flags do the same job?
| Task | curl | Wget |
|---|---|---|
| Save under the remote name | curl -O URL | wget URL |
| Print to the terminal | curl URL | wget -qO- URL |
| Follow redirects | -L | on by default |
| Resume a partial file | -C - | -c |
| Retry after a timeout | --retry 3 | on by default |
| Continue a broken download by itself | no: --retry-all-errors starts over | on by default |
| Retry a 503 | --retry 3 | --retry-on-http-error=503 |
| Fail on an HTTP error | -f | on by default |
| Set the User-Agent | -A "name" | -U "name" |
| Use an HTTP proxy | -x http://host:port | -e use_proxy=yes -e https_proxy=http://host:port |
| Proxy login | -U user:pass | --proxy-user=user --proxy-password=pass |
| SOCKS5 proxy | -x socks5h://host:port | not supported |
| Skip the certificate check | -k | --no-check-certificate |
| Many files at once | -Z | not supported |
| Copy a whole site | not supported | -r |
The certificate options do what their names say. curl's manual: -k "makes curl skip the verification step and proceed without checking". Our page on curl -k covers when that is safe.
Which should you use?
- To download files and walk away, use Wget. It follows redirects and picks up broken downloads without extra options, and copies whole sites with
-r. - For APIs, scripts and control over the request, use curl. Headers, uploads and parallel transfers are all built in.
- Behind a SOCKS5 proxy, use curl. Behind an HTTP proxy, either one works.
What this page could not check
- One server, curl 8.5.0 and GNU Wget 1.21.4. Newer versions may differ: curl 8.17.0 changed how a retry treats a partial download.
- Our test servers and proxies are small ones of our own. Real servers and proxies may answer differently.
- The big-file times depend on the network path and on the server's other work at that moment.
- We will run the tests again by 11 January 2027.
Sources
- Daniel Stenberg, curl vs Wget, read 11 October 2026: daniel.haxx.se.
- GNU Wget manual, version 1.21.4 as installed on our server, read 11 October 2026: gnu.org.
- curl, man page, -L, --retry, --retry-all-errors, -C, -Z, -k and the SOCKS options, read 11 October 2026: curl.se.
- curl, changelog for 8.5.0 and 8.17.0, read 11 October 2026: curl.se.
- RFC 1928, SOCKS Protocol Version 5, IETF, March 1996: rfc-editor.org.
- RFC 9110, HTTP Semantics, 206 Partial Content, IETF, June 2022: rfc-editor.org.
- FoxIO, JA4 technical details, read 11 October 2026: github.com.
- Our own tests on our server, 11 October 2026: defaults.sh at 16:06 UTC, broken.sh at 16:08 UTC, proxies.sh and mirror.sh at 16:11 UTC, speed.sh at 16:18 UTC, more.sh at 16:24 UTC.



