Pick the wrong tool in a script and you can end up with a nightly job that saves a 404 error page as your backup file, and still reports success. That is the real curl vs wget question. Both download things, sure, but they behave differently the moment something goes wrong, and that difference is what bites people on servers.
| Use wget for | Grabbing files, resuming big downloads, mirroring a site or directory |
| Use curl for | APIs, custom headers, POST/PUT requests, scripts, debugging HTTP |
| Default output | wget writes to a file, curl prints to the terminal |
| Installed by default | curl almost everywhere; wget on Ubuntu 24.04 and older, but not on fresh Ubuntu Server 25.10+ or Rocky minimal |
curl vs wget: The Short Answer
The difference between curl and wget comes down to what each command-line tool was built for: wget downloads files, curl transfers data with full control over the request. GNU Wget is a non-interactive downloader. You point it at a URL, it saves the file, done. It follows redirects on its own, retries when the network drops, and can crawl a whole directory tree if you ask it to. It was built to work unattended, so it is happy running in the background on a flaky link.
curl is more of a Swiss army knife for talking to servers. It sends whatever request you build (any method, any headers, any body) and shows you the response. Saving to a file is just one option among many. Under the hood it is a thin front end to libcurl, the same library baked into a huge number of apps, which is why so many API docs show curl examples. It is also pre-installed on macOS and Windows 10 and 11, while wget is not. If you want the full flag list, our curl command guide with examples goes deeper.
Whether people search wget vs curl, curl vs wget, or dig through curl vs wget Reddit threads, most admins end up in the same place: they keep both installed and stop treating it as a rivalry. wget for "just get me that file", curl for everything that involves a conversation with a server.
Key Takeaway:
How curl and wget Actually Work Under the Hood
Most comparisons stop at "curl supports more protocols". True, but not that useful. The differences that matter day to day come from how each tool was designed to handle output, redirects and errors.
What wget does when you hit Enter
wget resolves the host, opens the connection, sends a GET request, and streams the body straight to a file named after the last part of the URL. If the server answers with a 301 or 302, wget follows it automatically, up to 20 redirects by default. If the connection drops, it retries (also 20 tries by default). If the server sends back any 4xx or 5xx error, like a 404 or 500, wget prints the error and exits with code 8. (An exit code is the number a command hands back when it finishes: 0 means success, anything else means trouble, and $? shows you the last one.) For a cron job, that last part is gold.
What curl does when you hit Enter
curl sends the request and dumps the response body to stdout, which just means it prints in your terminal. No file, no redirect following, no retries unless you add -O or -o, -L and --retry. Here is the one that catches people: by default curl treats a 404 page as a perfectly good response. It got bytes back, so it exits 0. You need -f (--fail) or --fail-with-body to make HTTP errors count as failures. With either flag, curl exits with code 22 when the server returns 400 or above. One more quiet default: curl has no overall time limit, so a stalled server can hang a cron job for hours unless you add --connect-timeout and --max-time.
Recursion vs pipes
wget can parse HTML and follow links (-r, --mirror), which curl simply does not do. It can control recursion depth with -l, stay below a folder with --no-parent, and it respects robots.txt by default when crawling. curl, on the other hand, is built for pipes. It writes to stdout so you can feed JSON into jq or text into grep without temp files. Two philosophies, both valid.
Concept:
curl speaks a long list of protocols (HTTP, HTTPS, FTP, SFTP, SCP, SMTP, IMAP, LDAP, MQTT and more), while classic wget sticks to HTTP, HTTPS and FTP. If the protocol names are fuzzy, our networking protocols explained piece is a quick refresher. Also worth knowing: HTTP/2 and HTTP/3 support in curl depends on how your distro compiled it, so run curl --version and check the Features line before assuming. The official curl documentation lists every supported protocol.
Practical curl vs wget Commands on Ubuntu and Rocky Linux
Whether you are on a home lab box or a fresh cloud server (if you are still choosing where to host one, our Cloudways vs Vultr comparison is a decent starting point), the first step is the same: check what is installed, then add whatever is missing.
curl vs wget on Ubuntu: install and check both tools
Ubuntu 24.04 and 22.04 server images ship both. That changed with Ubuntu Server 25.10: fresh installs no longer include wget and ship wcurl instead, a small wrapper bundled with curl that handles simple wget-style downloads. Upgraded servers keep wget and it is still in the archive, so on a fresh 26.04 box, check before you assume.
LinuxTeck
which curl wget
curl --version | head -n 1
wget --version | head -n 1
# Install both if anything is missing
sudo apt update
sudo apt install -y curl wget
# Ubuntu 25.10 and later: wcurl ships with curl for simple downloads
wcurl https://example.com/files/app.tar.gz
Install and check both tools on Rocky Linux 9 and 10
Rocky is the one that surprises people. curl is there, wget often is not on a minimal install. Rocky 9 and 10 both default to curl-minimal, a stripped build with fewer protocols. Package commands differ a bit from Ubuntu, see dnf vs apt if you jump between both.
LinuxTeck
rpm -q curl curl-minimal wget
# wget is usually missing on minimal installs
sudo dnf install -y wget
# If curl-minimal is installed, swap in the full build for more protocols
sudo dnf swap -y curl-minimal curl
sudo dnf swap -y libcurl-minimal libcurl
curl --version | head -n 1
curl vs wget example: download the same file
Same job, two styles. Note how curl needs extra flags to behave like wget does out of the box.
LinuxTeck
wget https://example.com/files/app.tar.gz
# curl needs -O to save, -L to follow redirects
curl -L -O https://example.com/files/app.tar.gz
curl -L -o app.tar.gz https://example.com/latest
# Resume an interrupted download
wget -c https://example.com/files/rocky.iso
curl -C - -O https://example.com/files/rocky.iso
# Parallel downloads with curl (7.66 and later)
curl -Z -O https://example.com/files/a.tar.gz -O https://example.com/files/b.tar.gz
# Keep the filename the server suggests (download links that redirect)
curl -L -J -O https://example.com/download?id=42
wget --content-disposition https://example.com/download?id=42
Where curl wins: talking to APIs
This is the everyday stuff curl was built for. Headers, tokens, JSON bodies, and checking just the response headers without downloading anything. If you normally test APIs in Postman, think of curl as the terminal version of it: no GUI or saved collections, but it runs anywhere, fits in scripts, and Postman can export any request as a curl command. And if you have seen curl vs wget vs fetch comparisons, fetch is a different animal: it is the HTTP API in JavaScript (browsers and Node.js), or the downloader in FreeBSD, not a standard Linux command.
LinuxTeck
curl -I https://example.com
# POST JSON to an API with a token
curl -sS -X POST https://api.example.com/v1/servers \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_TOKEN" \
-d '{"name":"web01"}'
# Show timing for a request
curl -o /dev/null -s -w '%{http_code} %{time_total}s\n' https://example.com
Where wget wins: bulk and recursive downloads
In the mirror command, --convert-links rewrites links so the copy works offline, and --page-requisites pulls in the images and CSS each page needs.
LinuxTeck
wget --mirror --convert-links --page-requisites --no-parent https://example.com/docs/
# Recursive download two levels deep, only on sites you own or have permission for
wget -r -l 2 --no-parent -e robots=off https://example.com/files/
# Download every URL listed in a file into one folder
wget -i urls.txt -P /srv/downloads
# Run a big download in the background, log goes to wget-log
wget -b https://example.com/files/rocky.iso
tail -f wget-log
Authentication, cookies, proxies and uploads
Both tools handle logins, cookies and proxies, just with different flags. Full upload support is curl only (wget can send a raw body with --method=PUT --body-file, but no multipart forms), and so is native SOCKS proxy support, which matters if you do light web scraping or route traffic through a jump host. Rate limiting works in both and is handy on a shared link.
LinuxTeck
curl -u admin:$PASS -O https://example.com/private/report.csv
wget --user=admin --password=$PASS https://example.com/private/report.csv
# Save and reuse cookies
curl -c cookies.txt -b cookies.txt https://example.com/login
wget --load-cookies cookies.txt https://example.com/members/file.zip
# Custom user agent and proxy (socks5h also resolves DNS through the proxy)
# Both tools also honour the http_proxy, https_proxy and no_proxy variables
curl -A "Mozilla/5.0" -x socks5h://127.0.0.1:1080 https://example.com
wget --user-agent="Mozilla/5.0" -e use_proxy=yes -e https_proxy=http://proxy.example.com:3128 https://example.com/file.zip
# Upload a file over FTP, or as a browser-style form field (curl)
curl -T backup.tar.gz -u user:$PASS ftp://ftp.example.com/backups/
curl -F "file=@backup.tar.gz" https://upload.example.com/api
# wget can PUT a raw file body, nothing fancier
wget --method=PUT --body-file=backup.tar.gz https://upload.example.com/backups/backup.tar.gz
# Limit bandwidth
curl --limit-rate 2M -O https://example.com/files/rocky.iso
wget --limit-rate=2m https://example.com/files/rocky.iso
Verify downloads properly in scripts
Honestly, this is the section I care about most. A while back I had a cron job on a Rocky box pulling a database dump with plain curl -o. It "succeeded" every night for weeks. Turned out the URL had moved, curl happily saved a tiny HTML 404 page as db.tar.gz, exited 0, and nobody noticed until we actually needed a restore. One -f flag would have caught it on night one. Pair that with sane exit code handling in Bash and you sleep better.
Two more traps. wget with -O creates the output file before it knows whether the download worked, so a failed run leaves an empty file behind. And no script should write straight to the final path. Download to a .part file, check it, then mv it into place, so nothing ever reads a half-written backup.
LinuxTeck
curl -fsSL --connect-timeout 10 --max-time 600 --retry 3 --retry-all-errors \
-o /backup/db.tar.gz.part https://backup.example.com/db.tar.gz
echo "curl exit code: $?"
# wget: timeout per stage, exit code 8 on server errors
wget -q --timeout=30 --tries=3 -O /backup/db.tar.gz.part https://backup.example.com/db.tar.gz
echo "wget exit code: $?"
# Check against the published checksum, then move it into place
echo "$EXPECTED_SHA256 /backup/db.tar.gz.part" | sha256sum -c - && mv /backup/db.tar.gz.part /backup/db.tar.gz
curl vs wget: Full Side-by-Side Comparison
| Feature | curl | wget | Verdict |
|---|---|---|---|
| Default output | Prints to stdout | Saves to a file | wget for quick downloads |
| Follows redirects | Only with -L |
Yes, by default | wget, fewer surprises |
| Exit code on 404 | 0, unless -f is used |
8 (server error) | Use curl -f in scripts |
| Recursive download / mirroring | Not supported | Yes (-r, --mirror) |
wget |
| HTTP methods and headers | Any method, full control | GET/POST, plus --method for others |
curl |
| Protocols | 20+ (HTTP, FTP, SFTP, SCP, SMTP, MQTT...) | HTTP, HTTPS, FTP | curl |
| Resume downloads | -C - |
-c |
Tie |
| Retries on network failure | Only with --retry |
On by default | wget |
| HTTP/2 and HTTP/3 | Yes, depends on build | wget 1.x: no, wget2: HTTP/2 | curl |
| Library for developers | libcurl, used everywhere | None in wget 1.x | curl |
| Background downloads | Not built in | -b |
wget |
| File uploads | Yes (-T, --upload-file, forms) |
Limited (--method=PUT --body-file), no forms |
curl |
| Proxy support | HTTP, HTTPS, SOCKS4, SOCKS5 | HTTP and HTTPS proxies only | curl |
| Parallel downloads | -Z / --parallel |
wget 1.x: no, wget2: yes | curl |
| Cookies and authentication | -b, -c, -u |
--load-cookies, --user |
Tie |
| robots.txt | Not applicable | Respected when recursing | wget, crawl politely |
| Pre-installed on macOS / Windows | Yes / Yes | No / No | curl |
| Default on Ubuntu / Rocky | Yes / Yes | 24.04 and older: yes, 25.10+: no / often missing | curl is safer to assume |
| Timeouts | No total limit by default (--max-time) |
900s read timeout by default (--timeout) |
Set one explicitly |
Compatibility Note:
The wget vs wget2 split catches people out. Fedora 40 and later replaced wget with wget2 behind the same wget command. It is faster and supports HTTP/2, but it dropped FTP support and a number of legacy options. Those regressions, FTP in particular, are why Red Hat kept wget 1.x for the EL10 family, so Rocky Linux 10 scripts that rely on old wget flags keep working. If a script works on Rocky and breaks on a Fedora laptop, check wget --version first. Same story in Alpine containers, where wget is usually the BusyBox version with far fewer options. More on mixed-distro networking in our Linux network administration guide.
Best Practice:
Be careful with the classic curl ... | bash install one-liner. This comes up constantly in admin discussions, and the worry is fair: you run whatever the server sends, and a half-downloaded script can run half its steps. Download first, read it, check the checksum, then run it. That habit belongs on any server hardening checklist.
Red Flags: When curl or wget Downloads Go Wrong
These three show up again and again, usually at the worst possible time.
Common Mistake: Downloaded file is HTML, not your archive:
You get a 2 KB app.tar.gz and tar says it is not in gzip format. curl saved an error page or a redirect body. Check with file app.tar.gz and curl -sI URL to see the real status code, then rerun with curl -fL -O. The curl command examples cover the flags in more detail.
Security Warning: curl: (60) SSL certificate problem: unable to get local issuer certificate:
wget shows the same problem as "cannot verify certificate". Do not fix this with -k or --no-check-certificate in production, that just turns off TLS checks. Run curl -v https://example.com 2>&1 | grep -i issuer to see the chain, then refresh the CA store with sudo update-ca-certificates on Ubuntu or sudo update-ca-trust on Rocky. Background on why this matters: HTTP vs HTTPS explained.
Common Mistake: curl: (6) Could not resolve host:
wget reports it as "unable to resolve host address". Nine times out of ten this is DNS, not the tool. Test with getent hosts example.com and check resolvers with resolvectl status on Ubuntu or nmcli dev show | grep DNS on Rocky. If those fail too, work through our Linux DNS troubleshooting guide.
curl vs wget - FAQ
Q1: curl vs wget: which is faster?
For a single file, not in any way you will notice, both are limited by the network. curl can be quicker on servers that support HTTP/2 or HTTP/3, and wget2 adds parallel downloads. Test it yourself with time curl -sO URL vs time wget -q URL. Real speed gains usually come from the network side, see our Linux networking commands.
Q2: Can wget replace curl for API calls?
Partly. wget can send a POST with --post-data and add headers with --header, so simple calls work. wget can send other methods with --method, but anything beyond a simple call, like detailed response inspection or form uploads, is far easier in curl. For API work, just use curl -sS and save yourself the pain. Our curl examples have more request patterns.
Q3: Why does curl print the file to my terminal instead of saving it?
That is by design, curl writes to stdout so it can be piped into other tools. Add -O to keep the remote name or -o name to choose one. If you dumped a binary to the terminal and it looks garbled, type reset. It is one of the most common beginner surprises with curl.
Q4: Which one should I use in cron jobs and shell scripts?
Either works if you handle errors. With curl, always use -fsSL plus --retry; with wget, check that $? is 0 after the run. Log the output somewhere you will actually read. Our cron command guide shows how to redirect job output to a log.
Q5: Can curl replace wget completely?
For single downloads, yes: curl -fLO URL does the same job, and curl is the safer bet for portable scripts since it is on Ubuntu, Rocky Linux and macOS out of the box. What curl cannot do is recursive downloading or mirroring a website, there is no -r. For that you still want wget. On Ubuntu 25.10 and later, wcurl also gives you wget-style simple downloads on top of curl. Check what each distro ships with in dnf vs apt.
Q6: Which is better for FTP downloads, curl or wget?
Both handle FTP, and wget can even download an FTP directory recursively with wget -r ftp://host/dir/. curl wins when you need to upload, using curl -T file ftp://host/dir/, or when the server uses SFTP. One catch: wget2 dropped FTP, so on newer Fedora use curl. For secure transfers, an encrypted protocol beats plain FTP every time.
Final Thoughts: curl vs wget, Keep Both and Use Each Well
curl is for talking to servers, wget is for pulling files down. Once that clicks, picking one takes about two seconds.
Spend ten minutes today checking your scripts for plain curl -o without -f. Then get comfortable with the core Linux networking commands and the network administration basics, since most download failures end up being network problems anyway.
Further Reading on LinuxTeck:
curl Command in Linux with Examples - every common curl flag with working examples.
Bash Script Exit Codes and Error Handling - how to make scripts fail loudly instead of silently.
HTTP vs HTTPS Explained in Simple Terms - what TLS actually does for your downloads.
Linux DNS Troubleshooting Made Easy - fix the "could not resolve host" errors fast.
Linux Server Hardening Checklist - lock down the servers you are downloading to.
LinuxTeck - A Complete Linux Infrastructure Blog
LinuxTeck covers everything from beginner Linux commands to advanced Linux system administration and DevOps career guidance, written by practitioners for professionals working on Ubuntu, Rocky Linux, RHEL, and enterprise Linux environments every day.