Fetching a URL from a Linux shell can leave you deciding whether the response should go into a file or appear in the terminal. Wget and curl are command-line tools for transferring data to and from URLs, and they handle output differently by default.
This article compares which command fits common transfer tasks and shows how to download data or send a request.
TL;DR: wget vs curl
Use wget for saving files and retrieving linked web content, and use curl when you need control over a request or want to pipe a response into another command. Both can fetch URLs, but their defaults make different tasks convenient.
- wget saves a downloaded resource to a file by default. curl writes the response body to standard output.
- Use curl -o to choose a file name, or curl -O to use the remote name.
- Wget includes recursive retrieval and background-download options. curl focuses on transfers for the URLs and requests you specify.
- Use curl -L when you want it to follow HTTP redirects.
What are wget and curl?
GNU Wget is a command-line utility for non-interactive web downloads. curl transfers data to or from a URL, and its project also provides libcurl for applications. In this piece, I’ll compare them by task rather than assume every installation supports the same features.
Run curl --version to see curl’s protocols and build details, and use wget --version to inspect Wget’s build. Feature support can differ between GNU Wget, Wget2 and curl builds, so check installed versions before relying on an option across machines.
Which command should you use?
The table maps common transfer tasks to a starting point and explains the trade-off for each choice.
| Task | Better starting point | Reason |
|---|---|---|
| Save one file using its URL name | wget | It writes downloads to a local file by default. |
| Save a curl response to a chosen path | curl | The -o option names the destination. |
| Send form data or set request headers | curl | Its options expose request methods, headers and data. |
| Retrieve a site recursively | wget | Recursive retrieval is built into Wget. |
| Send a response into another command | curl | Its response body goes to standard output by default. |
| Continue an interrupted download | Either | Both have continuation options, with different syntax. |
A HEAD request is useful for checking response metadata before requesting the body. Servers or caches may handle HEAD and GET differently, so use GET when you need to confirm the returned content.
Use the site guide to the FTP command when you are moving files with FTP.
Inspect response metadata
The command below asks the server for response headers.
curl -I https://api.github.com/repos/curl/curl
Servers can omit Content-Type, Content-Disposition or Content-Length, so interpret the fields that actually appear in the response.
How to download and transfer data
These examples use GitHub’s public API for read-only transfers. The JSON POST URL is a placeholder. Replace it with the service you intend to call before sending data.
Download a file with wget
Wget saves a resource locally. Use -O to choose its output path, while lowercase -o names a log. In curl, lowercase -o writes the body and uppercase -O uses the remote name, so keep the tool name beside each flag when adapting a command.
wget https://api.github.com/repos/curl/curl
wget -O curl-project.json https://api.github.com/repos/curl/curl
When a URL points to a page rather than a direct file, the saved data may be HTML. Confirm that the URL serves the resource you expect before using the downloaded path in another command.
For more wget options and examples, see the Linux wget command guide. If a transfer has an IPv4 or IPv6 connectivity issue, the guide to forcing Wget to use IPv4 or IPv6 covers that narrower case. That guide also explains when the address family changes a connection attempt.
Save a curl response to a file
Use -O when the URL contains a useful filename. With -J and -O, curl can use a filename supplied by Content-Disposition, while an explicit -o path gives a script a predictable destination.
curl -o curl-project.json https://api.github.com/repos/curl/curl
curl -O https://api.github.com/repos/curl/curl
Send a request or pipe a response with curl
An API request can include a media type header and data in its body. This command shows the request shape. Replace the placeholder address with the endpoint you intend to call.
curl -H 'Content-Type: application/json' -d '{"status":"ready"}' https://api.example.com/items
The -d option sends a POST body. GNU Wget also provides --post-data and --post-file, so simple POST requests are not unique to curl. Use the method and media type specified by the API.
The pipe helps when a later command parses the response. Never send untrusted content directly to a shell or interpreter.
set -o pipefail
curl -fSs https://api.github.com/repos/curl/curl | python3 -m json.tool
In Bash, pipefail includes curl’s exit status instead of checking only Python’s. Curl’s -f treats HTTP error statuses as failures, while -sS suppresses progress and keeps error messages. json.tool formats JSON but does not check an API’s required fields.
For additional examples, see the curl download guide.
Quote the URL so the shell passes the ampersand as part of one argument.
curl -o results.json 'https://api.github.com/search/repositories?q=wget&per_page=1'
Use the shell’s quoting syntax when a URL itself contains a quote.
Download several known URLs
Put one URL on each line of the input file. Check the list before running it because each entry can trigger a network request.
wget -i urls.txt
After the batch finishes, check its exit status and expected files before letting a later step use them. Choose explicit destinations when another command depends on fixed paths.
Run a long Wget download in the background
Wget’s -b option starts the transfer in the background, returning the prompt while Wget continues.
wget -b https://api.github.com/repos/curl/curl
Check the log and output file when the transfer finishes. Keep short jobs in the foreground when a script needs to react to failure. The site’s nohup guide explains another way to keep a process running after a shell session ends.
These options reflect Wget’s download workflow. curl can still perform the transfers in a shell-managed background process, but job control then comes from the shell or process supervisor rather than a built-in recursive or background download mode.
Defaults and edge cases to check
Redirect behavior, HTTP status handling, filenames and continuation behavior can affect a transfer. Check those details before depending on the result in a script.
Redirects do not follow the same default
Wget follows HTTP redirects by default. curl needs -L to request the redirected resource.
curl -L -o redirect.json https://httpbin.org/redirect/1
I ran the redirect example in this refresh. It saved JSON with the final URL https://httpbin.org/get.
Follow redirects only when the destination is expected, especially for requests that include credentials or sensitive data.
A transfer can succeed while the HTTP request fails
curl’s fail option returns an error when the server sends an HTTP status of 400 or higher. Use it when a script must stop instead of passing a server-error page onward. Redirect handling is separate.
| Situation | Symptom | Response |
|---|---|---|
| Server returns HTTP 4xx or 5xx | Response body exists but request is unsuccessful | Record response status separately from the transfer exit code. |
| Batch or script calls several URLs | One file exists although a transfer failed | Check final status and expected output files. Do not trust file presence alone. |
Resume only when the server supports it
The continuation option requests more data after the local partial file.
| Tool | Continuation form | Requirement |
|---|---|---|
| wget | wget -c URL | Remote server must support range requests for continuation. |
| curl | curl -C – -O URL | Local partial file must match the remote resource and the server must support ranges. |
If the remote object changed during interruption, remove or rename the partial file before retrying, because appending bytes from two versions corrupts the result.
Recursive retrieval is a different job from fetching a URL
Use recursion only when the site permits retrieval of its linked pages.
| Task | What happens | Choice |
|---|---|---|
| Fetch a known URL | One specified resource is requested | Either tool can work. Choose by output and request needs. |
| Retrieve linked pages recursively | More URLs may be discovered and fetched | Wget has recursive retrieval options. |
Confirm access rules and decide whether a static offline copy is complete. Wget does not execute JavaScript, so dynamic interfaces may not be represented.
Retries do not replace an error policy
Retries help with transient connection failures, not malformed requests or permanent HTTP errors.
| Failure type | What the command sees | Safer handling |
|---|---|---|
| Connection interruption | Transfer may stop before all bytes arrive | Use a bounded retry policy, then verify the resulting file. |
| HTTP error response | Server returned a response that may include an error body | Check status handling and exit status, then decide whether retrying fits the service. |
| Invalid or changed resource | A retry can fetch different content or repeat a permanent failure | Check the URL and resource identity before retrying. |
wget --tries=3 https://api.github.com/repos/curl/curl
curl --retry 3 -o curl-project.json https://api.github.com/repos/curl/curl
These examples cap attempts for a file URL. Set a timeout as well when a scheduled task must finish within a defined window, then report the final exit status to the calling script.
Limit transfer bandwidth without confusing it with API quotas
Both tools can cap transfer speed. This is a byte-rate ceiling, not a per-request delay or API quota.
wget --limit-rate=300k https://api.github.com/repos/curl/curl
curl --limit-rate 300k -o curl-project.json https://api.github.com/repos/curl/curl
Keep credentials out of command arguments
Use documented authentication methods, and keep secrets out of plain command strings.
| Risk | Why it matters | Safer practice |
|---|---|---|
| Credentials in a URL | They can appear in process information or logs. | Use the client’s documented authentication mechanism and protect any credential file with restrictive permissions. |
| Secrets in shell history | A command may remain in the user’s history file. | Use a credential store or another protected input mechanism suited to the environment. |
| TLS certificate error | The URL, local trust store or server certificate may need attention. | Correct the certificate configuration and keep verification enabled. |
Conclusion
Start with the expected result: a file on disk, a response passed to another program, or a request with custom headers or data. Pick the tool whose defaults fit, then check the exit status and output before the next step consumes them.
For further reference, read the GNU Wget manual and the curl command manual. To learn how to use the manual-page viewer locally, see the site’s guide to the man command.
FAQ
These answers cover the defaults and choices that tend to cause confusion.
Can wget and curl transfer files over FTP?
GNU Wget supports FTP retrieval, and curl supports FTP when its build includes that protocol. Check the installed build if FTP support is required.
Why should I keep a password out of a transfer URL?
A password in a command-line URL can be visible in process listings or logs. Use a documented authentication method and protect any credential input.
Can both tools send a POST request?
Yes. Both support simple POST requests, but their options differ. Match the API’s required method and content type.
Is Wget2 the same as GNU Wget?
No. Wget2 is a separate release line. Check which executable and local manual your system provides before relying on an option.
