Two command-line tools that both fetch things over HTTP, installed on nearly every machine, endlessly compared and rarely distinguished usefully.
The most reliable source on the difference is, pleasingly, the comparison curl's own maintainer publishes — a document that includes an explicit section on what wget does better than curl. Every factual claim below traces back to that document or to the projects' own manuals rather than to anyone's impression, which matters in a comparison this frequently written from memory.
We are Geonode, we sell proxies, and the honest note is short: neither tool needs a proxy for the great majority of what people use them for. Fetching a file, calling an API, checking whether a service responds — none of it requires one. There is a genuine and under-documented difference between the two on proxy support, and it has a section below, but if you arrived here trying to choose a downloader you can safely ignore it.
The useful summary is that these tools were built with different mental models, and almost every surface difference follows from that. curl behaves like cat — it fetches something and writes it to standard output. wget behaves like cp — it fetches something and writes it to a file. That is the maintainer's own framing, and once you have it, the redirect defaults, the flag differences and the recursion capability all stop being arbitrary.
The short recommendation, for anyone who wants it before the detail: use wget when you want files on disk, and curl for everything else.
The One-Sentence Difference
curl works, in its maintainer's words, "like the traditional Unix cat command". wget works "more like cp".
That single distinction explains most of what follows.
What It Means in Practice
curl https://example.com/file.txt # prints the contents to your terminal
wget https://example.com/file.txt # saves file.txt to the current directory
Neither is wrong. They are answering different questions.
curl's design assumes you want the data, and that what happens to it next is your business — pipe it, parse it, redirect it, read it. Writing to standard output is the composable choice, and it is why curl slots naturally into shell pipelines.
wget's design assumes you want the file. It picks the filename, creates it, shows a progress bar, and finishes with something on disk.
Why the Consequences Are Larger Than They Look
Once a tool's job is "get the file onto the disk", a whole set of behaviours become obviously correct: follow redirects, because the file has moved; retry on failure, because the goal is the file rather than the attempt; resume a partial download, because half a file is not the job. wget does all of these by default.
Once a tool's job is "perform this transfer and give me the result", the correct behaviours are different: report what happened rather than deciding for me, do exactly what was asked, and let the caller handle the rest. curl reports the redirect and stops, because following it was not what you asked for.
Neither set of defaults is better. They are consistent with different purposes, and most of the frustration people have with either tool comes from expecting the other one's assumptions.
What curl Does That wget Does Not
From the maintainer's comparison, and it is a substantial list.
It Is a Library First
curl ships libcurl, described as having "a stable API that can be used by each and everyone". This is the most consequential difference and the least visible one from a terminal.
libcurl is embedded in an enormous quantity of software — language bindings, applications, devices. The command-line tool is in some sense a demonstration of the library. wget is a program; curl is a program on top of a library that other programs use.
If you have ever used PHP's cURL functions or a language HTTP binding built on libcurl, you have used curl without running curl.
Far More Protocols
The published list is long: "FTP(S), GOPHER(S), HTTP(S), SCP, SFTP, TFTP, TELNET, DICT, LDAP(S), MQTT, FILE, POP3(S), IMAP(S), SMB(S), SMTP(S), RTMP, RTSP and WS(S)".
wget covers HTTP, HTTPS and FTP. For the web that is usually enough. For anything involving mail protocols, SFTP, MQTT or WebSocket, curl is the only one of the two in the conversation.
Newer HTTP Versions
curl supports HTTP 0.9, 1.0, 1.1, 2 and 3. If you need to test how a server behaves over HTTP/2 or HTTP/3 specifically, that is a curl job.
More Proxy Types
curl supports HTTPS proxies and SOCKS4 and SOCKS5 proxies. This is listed among the things curl has that wget does not, and it has real consequences — see the proxy section below.
Parallel Transfers
curl can run multiple simultaneous transfers with -Z. Useful when fetching many small resources where the latency of doing them one at a time dominates.
Bidirectional Transfer and Form Uploads
Sending data, not just receiving it. Multipart form uploads, PUT, arbitrary methods. wget is fundamentally a retrieval tool; curl is a transfer tool, and uploads are a first-class case.
It Is Already Installed on More Machines
curl ships pre-installed on macOS and on Windows 10 and 11. On a Windows machine with nothing added, curl is available and wget generally is not — which matters more than it should for cross-platform scripts.
What wget Does That curl Does Not
Also from curl's own maintainer, which is what makes this list worth trusting.
Recursive Download
"Wget's major strong side compared to curl is its ability to download recursively."
This is the big one and it is not a small feature. wget can follow links from a page and download what it finds, to a specified depth, converting links for local browsing as it goes. Mirroring a documentation site for offline reading is a single command.
wget -r -np -k -p https://example.com/docs/
Recursive, no parent directories, convert links for local viewing, fetch page requisites like images and stylesheets.
curl cannot do this at all. curl fetches URLs you give it. It does not parse HTML, does not discover links, and has no concept of a site. If your job is "copy this section of a website", wget is the answer and there is no curl equivalent worth attempting.
Resuming Broken Transfers
wget "can recover from a prematurely broken transfer and continue downloading". With -c, an interrupted download resumes from where it stopped.
curl can do this too with -C -, but wget's behaviour around retries and resumption is more automatic and more forgiving — which matters when the transfer is large and the connection is not reliable.
It Needs No Options for the Obvious Case
wget downloads a file with no flags. curl "needs -o or -O" to write to a file rather than the terminal.
For the single most common task anyone performs with either tool — download this file — wget is the shorter command, and being shorter is a real advantage in a tool you use daily.
More Sensible Defaults for Downloading
wget "enables more features by default: cookies, redirect-following, time stamping".
Time stamping is worth noting specifically: with -N, wget only re-downloads a file if the remote version is newer. For scheduled synchronisation of a set of files, this is exactly the right behaviour and curl has no direct equivalent.
Licence
wget is GPL v3; curl is MIT. If you are embedding either into a product, that difference is likely to matter more than any feature on this page.
Defaults That Cost You Time
The differences most likely to produce a confusing afternoon.
Redirects
wget follows redirects by default. curl does not.
This is the single most common source of "why did curl return nothing". The server replied with a 301, curl reported it and stopped, and standard output looks empty.
curl -L https://example.com/moved # follow them
wget https://example.com/moved # already following them
It is not an oversight in curl. Following a redirect means making a request you did not ask for, to a host you did not name, and curl's model is to do what it was told and report the rest. wget's model is to get the file, and the file moved.
Output Destination
Covered above and worth repeating because it catches people constantly. curl URL prints; wget URL saves.
Error Handling
Both have a quirk. curl treats a 404 as a successful transfer and exits zero — the transfer worked, the server answered. Use -f to make HTTP errors produce a non-zero exit status.
wget exits non-zero on HTTP errors by default, which is the more intuitive behaviour for a downloader.
In scripts: curl -sSf is the combination worth remembering, and its absence is a common reason a broken job appears to be working.
Retries
wget retries by default. curl does not unless you ask with --retry.
Again consistent with the models: a downloader should persist, a transfer tool should report.
The Practical Advice
If you are writing anything automated, set the behaviour explicitly rather than relying on either tool's defaults. curl -sSfL --max-time 30 states what you want. So does wget --tries=3 --timeout=30. Explicit commands survive being read by someone else in a year.
Side by Side
| Task | curl | wget |
|---|
| Print to terminal | curl URL | wget -O - URL |
| Save to file | curl -O URL | wget URL |
| Save with chosen name | curl -o name URL | wget -O name URL |
| Follow redirects | curl -L URL | default |
| Resume a download | curl -C - -O URL | wget -c URL |
| Headers only | curl -I URL | wget --spider -S URL |
| Custom header | curl -H "K: V" URL | wget --header="K: V" URL |
| Basic auth | curl -u user:pass URL | wget --user=u --password=p URL |
| POST data | curl -d "a=b" URL | wget --post-data="a=b" URL |
| Quiet | curl -s URL | wget -q URL |
| Mirror a site | not possible | wget -m URL |
| Upload a file | curl -T file URL | not possible |
| Use SOCKS5 | curl -x socks5h://host URL | not natively |
| Parallel transfers | curl -Z ... | not supported |
Reading the Table
The symmetry holds for the everyday operations — most tasks have a direct equivalent, with different flag spellings that are annoying rather than important.
The four rows marked as not possible are where the choice is actually made. Mirroring a site is wget only. Uploading is curl only. SOCKS proxying is curl only. Parallel transfers are curl only.
If your task is in one of those rows, the comparison is over and you can stop reading. If it is not, either tool works and you should use whichever you type more fluently.
A Note on the Flag Collision
-O means different things in the two tools, and it is a genuine trap.
In curl, -O means "save using the filename from the URL" and -o name means "save as this name". In wget, -O name means "save as this name" and there is no need for the other.
So curl -O and wget -O are not equivalents, and a command translated carelessly between the two will do something unexpected.
Which to Use By Task
A decision list rather than a verdict.
Use wget when
You want a file on disk. No flags, progress bar, sensible defaults. This is the majority case for most people.
You want to mirror or recursively download. The only option. wget -m or wget -r with appropriate limits.
The connection is unreliable and the file is large. Automatic retries and -c resumption.
You are keeping a local copy in sync. -N timestamping downloads only what changed.
You want the shortest possible command for a straightforward download in a script someone else will read.
Use curl when
You are working with an API. Headers, methods, request bodies, and output that pipes into a JSON processor.
You need to send data, not just retrieve it.
You are debugging. -v and -w give you the request as sent, the response, and timing broken down by phase. This is curl's strongest everyday advantage and it is not close.
You need a protocol beyond HTTP and FTP.
You need SOCKS or HTTPS proxy support.
You are on Windows or macOS with nothing installed, where curl is present and wget usually is not.
You are writing something that will become code, since libcurl's bindings mean the shape of the command translates naturally.
Use Both
The honest answer for most working setups. They are small, they are free, and they are good at different things. Having both installed and reaching for whichever fits is not indecision — it is the correct outcome of them being genuinely different tools.
Proxies in Both
Our territory, and there is one real difference worth knowing.
curl
# HTTP proxy
curl -x http://proxy.example.com:8080 https://example.com
# With credentials
curl -x http://user:pass@proxy.example.com:8080 https://example.com
# SOCKS5, with the proxy resolving hostnames
curl -x socks5h://proxy.example.com:1080 https://example.com
curl supports HTTP proxies, HTTPS proxies and SOCKS4/SOCKS5, all through the same -x option with a scheme.
wget
# Via environment variables
export http_proxy="http://proxy.example.com:8080"
export https_proxy="http://proxy.example.com:8080"
wget https://example.com
# With credentials
wget --proxy-user=user --proxy-password=pass https://example.com
wget reads the standard proxy environment variables and has options for proxy credentials.
The Difference That Matters
wget has no native SOCKS support. curl's comparison lists SOCKS4 and SOCKS5 among the things curl supports and wget does not.
If your proxy is SOCKS-only, wget cannot use it directly. The usual workarounds are running wget through a tool like proxychains, or putting a local HTTP-to-SOCKS bridge in front of it — both of which work and both of which add a component that can fail quietly.
If you are choosing between the two and SOCKS is in your setup, that decides it.
The DNS Detail, Again
Worth repeating because it applies whenever curl is used with SOCKS. socks5:// resolves hostnames on your machine; socks5h:// sends the hostname to the proxy. The first leaks every host you visit to your local resolver even though the traffic is routed correctly, and can hand you a regionally wrong address for sites with location-dependent infrastructure.
Use socks5h:// unless you have a specific reason not to.
And the Part Where We Talk Ourselves Out of a Sale
If you are downloading a file, calling an API you have credentials for, or checking whether a service is up, you need no proxy at all. Proxies earn their place in these tools for geographic checking and for volume work bounded by per-address rate limits. For everything else they add latency, a failure point and a bill.
When Neither Is the Right Tool
Several common situations call for something else, and reaching for either tool wastes an afternoon.
The page is rendered by JavaScript. Both tools fetch what the server sends. If the content is assembled in the browser afterwards, you get an empty shell and no flag fixes it. You need a headless browser — Playwright, Puppeteer or similar.
You need to interact with the page. Clicking, scrolling, filling forms, waiting for something to appear. Same answer.
You are building something maintainable in code. Shelling out to curl from an application is a common shortcut that ages badly. Use your language's HTTP library, or libcurl's bindings, and get proper error handling.
You need to sync a directory both ways. rsync is the tool, and it is dramatically better at it than recursive wget.
You are transferring between servers you control. scp, rsync or sftp — purpose-built, faster, and they handle permissions and partial transfers properly.
You need to inspect or modify traffic in flight. An intercepting proxy tool is the right instrument.
You are downloading media from a video platform. Purpose-built tools handle the manifest parsing and stream assembly that neither of these attempts.
The data is offered another way. An API, a bulk download, a public dataset, an RSS feed. Checking takes ten minutes and frequently ends the project before it starts.
The Recursive Download Caution
One specific warning about wget -r, which is powerful and easy to point at something you did not intend.
Without -np, it can climb into parent directories. Without --level, it can descend a long way. Without --wait, it will request as fast as the server answers, which is a rude thing to do to someone else's infrastructure and a good way to be blocked.
At minimum: wget -r -np --level=3 --wait=1 URL. And check robots.txt and the site's terms first — wget respects robots.txt by default, and disabling that is a decision rather than a convenience.
People Also Ask
What is the difference between curl and wget?
curl works like cat — it fetches and writes to standard output. wget works like cp — it fetches and saves a file. curl supports far more protocols, uploads, SOCKS proxies and parallel transfers; wget can download recursively and mirror sites, which curl cannot do at all.
Is curl better than wget?
Neither is better. curl is a transfer tool with a library behind it and a far wider protocol range; wget is a downloader with better defaults for downloading and unique recursive capability. Most people benefit from having both.
Can curl download recursively like wget?
No. curl fetches URLs you give it and does not parse HTML or discover links. Recursive download and site mirroring are wget-only, and curl's own maintainer names this as wget's major strength.
Why does curl not follow redirects?
By design. curl reports what the server said rather than making requests you did not ask for. Add -L to follow. wget follows by default because its goal is retrieving the file, and the file moved.
Which is faster, curl or wget?
For a single transfer, the difference is negligible — both are limited by the network. curl can run multiple transfers in parallel with -Z, which makes it meaningfully faster when fetching many small resources.
Does wget support SOCKS proxies?
Not natively. curl supports SOCKS4, SOCKS5 and HTTPS proxies; wget uses the standard HTTP proxy environment variables. For SOCKS with wget you need a wrapper such as proxychains or a local bridge.
Which should I use in a script?
Either, with explicit options. For APIs and anything where you inspect the response, curl -sSfL --max-time 30. For fetching files, wget --tries=3 --timeout=30. Do not rely on either tool's defaults in automation.
Is curl or wget installed by default?
curl ships pre-installed on macOS and Windows 10 and 11. wget is standard on most Linux distributions but frequently absent on macOS and Windows. For cross-platform scripts, curl is the safer assumption.
Wrapping Up
The comparison resolves faster than its popularity suggests, because the two tools were built around different verbs.
curl transfers. wget downloads. curl writes to standard output like cat, does exactly what it was asked, and reports the rest — which is why it does not follow redirects, does not retry, and needs a flag to write a file. wget writes to disk like cp, and everything it does by default follows from the goal being a file that exists at the end.
Four capabilities decide the choice outright, and if your task involves one of them the rest of the comparison is irrelevant. Recursive download and site mirroring are wget only — curl's own maintainer names it as wget's major strength and curl has no equivalent. Uploads, SOCKS proxies and parallel transfers are curl only.
Outside those, both work, and the practical differences are flag spellings and defaults. Watch the -O collision when translating commands between them, because it means opposite things in each. And in anything automated, state your intentions explicitly rather than relying on either tool's assumptions — curl -sSfL --max-time 30 and wget --tries=3 --timeout=30 are both commands that will still make sense to whoever reads them next year.
On proxies: we sell them and most uses of these tools need none. Where you do need one, curl's support is broader, and the detail that matters more than the tool is using socks5h:// rather than socks5:// so your hostname lookups travel the same route as your traffic.
The honest recommendation is to install both. They are small, they are free, and the argument over which is better has been settled for years by the fact that most working machines have both.