---
title: wget vs curl: What's the Difference? | Relic
description: wget vs curl compared honestly: wget is a recursive downloader, curl a protocol swiss army knife. What each does best, with side-by-side commands for common tasks.
canonical: https://relic.space/blog/wget-vs-curl
source: https://relic.space/llms.txt
---
[Developer cheat sheets](https://relic.space/blog/topics/dev-reference)

# wget vs curl: what is the difference?

wget and curl both fetch things over HTTP, and there the resemblance mostly ends. One is a recursive downloader; the other is a protocol swiss army knife. Here is what each is genuinely better at, with side-by-side commands.

[JGJordan Gibbs](https://relic.space/authors/jordan-gibbs) July 10, 2026 7 min readUpdated September 12, 2026

## What is the difference between wget and curl?

wget is a downloader and curl is a data transfer tool. wget can crawl links, mirror a whole site and resume a broken download on its own. curl makes one request at a time with full control over headers, methods and bodies, speaks many more protocols, and prints the response to stdout.

wget and curl are the two command-line tools everyone reaches for to move data over a network, and they get lumped together because both can download a file with one line. But they were built for different jobs, and knowing which job you have makes the choice obvious. wget is a downloader; curl is a data-transfer tool. That one distinction explains almost every difference below.

## What is each tool built for?

wget was designed to retrieve files, especially in bulk and especially unattended. It can follow links, mirror an entire site, resume a broken download and keep retrying on a flaky connection, all on its own. It is the tool you want when the goal is “get these files onto disk,” and it is happy to run in a script with no hand holding.

curl was designed to transfer data, in either direction, over an unusually long list of protocols. It is built on **libcurl**, a library that countless other programs use under the hood, and it exposes fine control over a single request: headers, methods, authentication, upload bodies, cookies. It is the tool you want when the goal is “make exactly this request and show me the response.”

There is also a quieter difference in how they report trouble. [curl does not treat an HTTP response code as a failure](https://curl.se/docs/manpage.html), so a 404 or a 500 still counts as a successful transfer; you print the error page and the exit code stays zero unless you add **\-f**, which makes curl exit with error 22 and print nothing on any response of 400 or above. wget, built for unattended jobs, is stricter: a failed download is a non-zero exit, which is exactly what a script watching for failures wants. That single behavioral gap bites in CI, where a curl step can look green while quietly downloading an error page.

## What can each tool do that the other cannot?

The clearest way to keep them straight is by their non-overlapping strengths.

### wget’s territory

* **Recursive mirroring.** Point wget at a URL with the right flags and it will crawl links and pull down a whole directory or site. curl has no equivalent; it fetches the URLs you name and nothing more.
* **Resume by default is easy.** wget is built around interrupted downloads: it can retry, resume a partial file, and keep going until the transfer completes.
* **Unattended by nature.**Continue on error, log to a file, wait between requests: wget’s defaults suit long batch jobs left running.

### curl’s territory

* **Many protocols.** Beyond HTTP and FTP, curl speaks HTTP/2 and HTTP/3, plus [SFTP, SCP, SMTP, IMAP, LDAP and a dozen others](https://curl.se/docs/features.html). [wget sticks to HTTP, HTTPS and FTP](https://daniel.haxx.se/docs/curl-vs-wget.html), and it speaks only HTTP/1.0 and HTTP/1.1.
* **Uploads and complex requests.** curl sends POST and PUT bodies, custom headers, form data and file uploads with ease. It is the standard way to poke an API from a shell.
* **A library behind it.** libcurl means the exact behavior you script on the command line is available inside applications in dozens of languages.
* **Writes to stdout by default.** curl prints the response to your terminal, which makes it a natural pipeline stage feeding into [jq](https://relic.space/blog/jq-cheat-sheet) or grep. wget saves to a file by default instead.

## Side by side

|                              | curl                     | wget              |
| ---------------------------- | ------------------------ | ----------------- |
| Default action               | Print response to stdout | Save file to disk |
| Recursive download / mirror  | No                       | Yes               |
| Resume a partial download    | Partial                  | Yes               |
| HTTP/2 and HTTP/3            | Yes                      | No                |
| Protocols beyond HTTP/FTP    | Yes                      | No                |
| Upload / POST bodies / forms | Yes                      | Partial           |
| Backed by a reusable library | Yes                      | No                |
| Follow redirects             | With -L                  | By default        |

The resume and upload cells are marked partial for a reason. wget can resume with **\-c**, and [curl can resume with **\-C -**, which works out the offset for you](https://curl.se/docs/manpage.html), so both do it. The difference is that wget retries and picks up a dropped transfer on its own, while with curl you run the command again. Likewise wget can send basic POST data with **\--post-data**, but curl is where uploads and complex request bodies actually live.

## The same task, in both tools

Most of the day-to-day overlap comes down to a handful of tasks, each written both ways below.

### Download a file

download to disk

curl -O https://example.com/archive.tar.gz     save with the remote filename
curl -o out.tar.gz https://example.com/archive.tar.gz   save under a chosen name
wget https://example.com/archive.tar.gz         saves as archive.tar.gz by default

Note the two curl flags: lowercase **\-o** takes a filename you supply, while uppercase **\-O** reuses the name from the URL. wget just saves under the remote name with no flag at all, which is the whole point of it.

### Resume an interrupted download

resume a partial file

curl -C - -O https://example.com/big.iso     resume from where it stopped
wget -c https://example.com/big.iso           continue the partial file

### POST JSON to an API

This is curl’s home turf. You set the method, a content-type header, and the body.

POST a JSON body

curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Ada", "role": "admin"}'

wget can send the same body with **\--post-data** and a header flag, but it is clumsier and rarely worth it. If you are talking to an API, use curl.

the wget equivalent, for completeness

wget --header="Content-Type: application/json" \
  --post-data='{"name": "Ada", "role": "admin"}' \
  https://api.example.com/users

### Follow redirects

follow 3xx redirects to the final URL

curl -L https://example.com/short-link     -L is required; curl does not follow by default
wget https://example.com/short-link         wget follows redirects on its own

This is a common curl surprise: without **\-L**, curl shows you the redirect response itself rather than the page it points to. wget follows the chain without asking.

### Save and reuse cookies

write cookies out, then send them back

curl -c cookies.txt https://example.com/login     -c writes received cookies to a jar
curl -b cookies.txt https://example.com/account   -b sends the saved cookies

wget --save-cookies cookies.txt --keep-session-cookies https://example.com/login
wget --load-cookies cookies.txt https://example.com/account

### Inspect response headers

When you are debugging why a request behaves oddly, the headers tell the story. curl shows them with **\-I** for a headers-only request, or **\-i**to include them above the body. wget writes the server’s response headers with **\-S**.

see the response headers

curl -I https://example.com           just the headers (a HEAD request)
curl -i https://example.com           headers, then the body
wget -S --spider https://example.com  print headers without downloading

That **\--spider** flag on wget is a quiet way to check whether a URL exists without pulling the file, which is useful in a script that just needs a yes or no.

### Mirror a whole site

This is the task with no curl equivalent. [wget’s **\--mirror** turns on recursion and time-stamping, sets infinite recursion depth and keeps FTP directory listings](https://manpages.debian.org/trixie/wget/wget.1.en.html), all in one flag, and the companion options rewrite links and pull in the page assets so the copy works offline.

wget only: recursive offline copy

wget --mirror --convert-links --page-requisites https://example.com/docs/

Read it as: mirror the site, rewrite links so they point at your local copy, and grab the images, stylesheets and scripts each page needs. There is simply no way to express this in curl, which is the single sharpest line between the two tools.

## Which should you use, wget or curl?

Let the shape of the task pick the tool.

* **Reach for wget** when you want files on disk: grabbing a release archive, mirroring a docs site, or running a long unattended download that might drop and need to resume.
* **Reach for curl** when you are working with a request: hitting an API, sending JSON, inspecting headers, testing an endpoint, or piping a response into another tool. It is also the one to learn if you script in a language that ships libcurl bindings.

In practice most developers keep both installed and use them for exactly these two moods. If you find yourself copying the same curl invocation for an API you hit constantly, that is a snippet worth keeping rather than rebuilding; the same goes for the jq filter you pipe it through. The sibling [jq cheat sheet](https://relic.space/blog/jq-cheat-sheet) pairs naturally with curl, and the cluster pillar, [the regex cheat sheet](https://relic.space/blog/regex-cheat-sheet), covers the pattern-matching you end up doing on the output. For copying these commands around a shell without mangling them, see [copy and paste in the terminal](https://relic.space/blog/copy-and-paste-in-the-terminal).

## Sources

* [curl man page](https://curl.se/docs/manpage.html), curl project
* [Features: what curl can do](https://curl.se/docs/features.html), curl project
* [curl vs Wget](https://daniel.haxx.se/docs/curl-vs-wget.html), Daniel Stenberg, curl maintainer
* [wget(1) manual page](https://manpages.debian.org/trixie/wget/wget.1.en.html), Debian manpages

JG

Written by

[Jordan Gibbs](https://relic.space/authors/jordan-gibbs)Founder, Relic

Jordan Gibbs is the founder of Relic, an end-to-end encrypted, permanent, searchable memory for everything you copy. He writes widely about AI, agents, and practical tooling on Medium, where he is read by tens of thousands, and builds privacy-first software. Here he covers how everyday tools like the clipboard actually work, and how to use them without handing your data to someone else.

[Medium](https://medium.com/@jordan%5Fgibbs)[GitHub](https://github.com/jordan-gibbs)[LinkedIn](https://www.linkedin.com/in/jordan--gibbs/)

Part of

[Developer cheat sheets](https://relic.space/blog/topics/dev-reference)
* [Regex cheat sheet· pillar](https://relic.space/blog/regex-cheat-sheet)
* [How to copy and paste in Vim](https://relic.space/blog/how-to-copy-and-paste-in-vim)
* [How to copy to the clipboard from the command line](https://relic.space/blog/copy-to-clipboard-from-the-command-line)
* [What AI crawlers fetch from a small open-source site: 31 days of logs](https://relic.space/blog/ai-crawlers-on-a-small-site)
* [JSONPath examples](https://relic.space/blog/jsonpath-examples)
* [jq cheat sheet](https://relic.space/blog/jq-cheat-sheet)
* [Subnet cheat sheet](https://relic.space/blog/subnet-cheat-sheet)
* [Port numbers list](https://relic.space/blog/port-numbers-list)
* [Ed25519 vs RSA](https://relic.space/blog/ed25519-vs-rsa)
* [TOML vs YAML](https://relic.space/blog/toml-vs-yaml)

Keep reading

[Pillar·10 minRegex cheat sheetThe regex pieces you actually reach for, character classes through lookarounds, with the flavor differences that bite you and a handful of patterns worth saving.Read ](https://relic.space/blog/regex-cheat-sheet)[9 minHow to copy and paste in Vimy copies and p pastes, but only inside Vim. How registers work, how to reach the system clipboard when your build says -clipboard, and how to copy out over SSH and in tmux.Read ](https://relic.space/blog/how-to-copy-and-paste-in-vim)[8 minHow to copy to the clipboard from the command linePipe any command into the clipboard on Mac, Linux, Windows and WSL. The commands for each, plus the newline and encoding problems that break pasted text.Read ](https://relic.space/blog/copy-to-clipboard-from-the-command-line)

developerclicomparison
