RelicRELIC
Developer cheat sheets

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.

Jordan Gibbs July 10, 2026 8 min read

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.

Two different philosophies

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. By default, curl treats an HTTP error like a 404 or a 500 as a successful transfer, because it did receive a response; you print the error page and the exit code stays zero unless you add -f to make curl fail on server errors. 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 each does that the other does not

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 more. wget sticks to HTTP, HTTPS and FTP.
  • 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 or grep. wget saves to a file by default instead.

Side by side

curlwget
Default actionPrint response to stdoutSave file to disk
Recursive download / mirror
Resume a partial download
HTTP/2 and HTTP/3
Protocols beyond HTTP/FTP
Upload / POST bodies / forms
Backed by a reusable library
Follow redirectsWith -LBy default
The resume and upload cells are marked partial for a reason. wget can resume with -c, and curl can resume with -C -, so both do it; the difference is that resuming is central to how wget is meant to be used and incidental to curl. 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 -ito 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, timestamping and infinite depth 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.

So which should you pick?

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 pairs naturally with curl, and the cluster pillar, the 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.

Written by
Jordan GibbsFounder, 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.

MediumGitHubLinkedIn
Part of
Developer cheat sheets
Keep reading
Pillar·9 min

Regex cheat sheet

The 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
8 min

JSONPath examples

JSONPath is XPath for JSON. One small store document, threaded through wildcards, slices, filters and recursive descent, with the honest note on how implementations vary.

Read
8 min

jq cheat sheet

jq is sed for JSON. The filters you use most, built up against one API response so every command has a visible result, from map and select to group_by and raw output.

Read
developerclicomparison