Skip to main content
Tech Tutorials & Programming

Published Aug 14, 2025 · Updated Sep 29, 2026

cURL vs Wget: Differences, Commands & When to Use Each

Compare cURL vs Wget for files, APIs, uploads, retries, resume, recursive downloads, mirroring, authentication, and proxies—with tested commands.

cURL vs Wget: Differences, Commands & When to Use Each

cURL and Wget can both retrieve a URL, but they are optimized for different jobs. cURL is a general data-transfer client with precise control over requests, headers, bodies, authentication, uploads, and proxies. GNU Wget is a retrieval tool designed around downloading files reliably and following links recursively.

The practical choice is usually simple: use cURL for APIs and custom requests; use Wget for unattended downloads and controlled site mirrors.

Quick Answer

Choose cURL when you need to call an API, inspect or customize an HTTP request, upload data, use several transfer protocols, or configure a proxy per command. Choose Wget when you need reliable file retrieval, URL-list downloads, automatic continuation, or recursive mirroring. Both can download files, resume transfers, retry failures, authenticate, and use HTTP proxies, but their defaults and strongest workflows differ.

Key Takeaways

  • cURL is the stronger request tool. It is better suited to APIs, custom headers, JSON, multipart forms, uploads, debugging, and per-request proxy settings.
  • Wget is the stronger retrieval tool. It saves downloads to disk by default and supports recursive retrieval, page requisites, link conversion, and mirroring.
  • Both tools can do more than their stereotype suggests. GNU Wget can send POST, PUT, headers, and request bodies; cURL can download and resume files.
  • Neither retries every failure safely by default. Define retryable status codes, attempt limits, time budgets, and whether the operation is safe to repeat.
  • Only Wget has built-in recursive downloading. The cURL command-line tool deliberately does not crawl links.
  • A proxy changes the connection route, not the command's purpose. Use one when the workflow needs its location, IP type, or network path—not as a substitute for permission or correct request logic.

cURL vs Wget Comparison

This guide covers the curl command-line client and classic GNU Wget 1.x. Wget2 is a separate project with a different feature set.

CapabilitycURLGNU Wget
Primary strengthTransfer one or more specified URLs with detailed request controlRetrieve files and recursively follow permitted links
Default response handlingWrites the response body to standard outputSaves the response to a local file
API requestsExcellent: methods, headers, JSON, form data, response inspectionPossible, but less ergonomic for complex API work
HTTP methodsGET, HEAD, POST, PUT, DELETE, PATCH, and custom methodsGET and POST plus custom methods through `--method`
Multipart file uploadBuilt in with `--form`Not supported by classic GNU Wget's body options
Raw body or file upload`--data`, `--data-binary`, and `--upload-file``--body-data` and `--body-file` with `--method`
Recursive downloadNo built-in recursive fetchYes, with `--recursive`
Website mirroringRequires another crawler or custom scriptBuilt in with `--mirror` and scope controls
Resume partial file`--continue-at -` or `-C -``--continue` or `-c`
Retries`--retry`, retry delay, retry time budget, optional status/error controls`--tries`, `--waitretry`, and optional retryable HTTP statuses
Redirect behaviorMust normally enable with `--location`Follows redirects by default, within its configured limit
AuthenticationBasic, bearer headers, `.netrc`, client certificates, and moreHTTP/FTP credentials, headers, `.netrc`, cookies, and more
Proxy configurationPer command or environment; HTTP(S) and SOCKS support depends on buildPrimarily HTTP, HTTPS, and FTP proxy environment/config settings
URL-list downloadsMultiple URLs, config files, and parallel mode in supported versionsDesigned for input files with `--input-file`
Best fitAPIs, debugging, uploads, automation, protocol flexibilityFiles, batch retrieval, continuation, offline copies, mirrors

Neither tool is universally better. A one-line file download may be equally reasonable with either. The differences become important when the workflow needs API ergonomics or recursive link traversal.

Tested Commands and Results

We tested the main behaviors against a controlled local HTTP origin and a separate authenticated local HTTP proxy. This made retries, byte ranges, authentication failures, uploads, and recursive links deterministic without sending test traffic to an unrelated site.

TestcURL resultWget result
JSON POSTServer parsed 36 bytes as JSONServer parsed the same 36-byte JSON body
Upload/body transferMultipart form contained the test fileRaw PUT body contained the test file
ResumeCompleted a 212,992-byte file from a 32,768-byte partial fileCompleted the same file from the same partial length
HTTP 503 retrySucceeded on attempt 3 after two failuresSucceeded on attempt 3 after two failures
HTTP Basic authenticationSucceededSucceeded
Authenticated HTTP proxyOrigin observed the proxy markerOrigin observed the proxy marker
Incorrect proxy passwordRejected; no successful direct fallback observedRejected; no successful direct fallback observed
Recursive mirrorNot applicableRetrieved 3 HTML pages, CSS, one referenced asset, and `robots.txt`

Tested versions were cURL 7.88.1, GNU Wget 1.21.3, and Python 3.11.2 on Linux x86_64. These are functional checks, not a speed comparison. Results can differ with another build, server, operating system, authentication scheme, or proxy.

What Is cURL?

cURL is a command-line data-transfer tool built on libcurl. The executable is written as curl, and its supported protocols depend on how that particular build was compiled. Run this to see the installed version, TLS backend, features, and protocols:

bash

cURL is especially useful when the request itself matters. It can set headers, choose methods, send JSON or forms, upload files, display response headers, authenticate, select a proxy, limit time, and expose detailed connection diagnostics.

For a broader introduction, see What Is cURL?.

What Is GNU Wget?

GNU Wget is a non-interactive network downloader. It is designed to retrieve resources without a user interface, continue through unstable connections, read batches of URLs, and traverse HTML or CSS links recursively.

Check the installed version and compiled capabilities with:

bash

Wget is not limited to simple GET requests. Current GNU Wget supports custom HTTP methods, headers, POST data, and raw body files. Its main advantage is still retrieval: local filenames, continuation, timestamping, recursive traversal, page requisites, and mirror-friendly behavior are central features rather than add-ons.

How to Install cURL and Wget

Check first; package availability and bundled versions vary by operating system.

Debian and Ubuntu

bash

Fedora

bash

macOS

macOS includes a system build of cURL. Check it with curl --version. GNU Wget is not included by default; one common installation route is Homebrew:

bash

Avoid replacing the operating system's cURL binary casually. If a newer feature is required, install a separate package and confirm which executable your shell resolves.

Windows

Current Windows installations normally provide curl.exe. Use the executable name explicitly in Windows PowerShell environments where curl could resolve to another command:

bash

GNU Wget is not a standard Windows component. Use a trusted package source or run it inside Windows Subsystem for Linux. Verify the publisher, package version, and checksums before installing a third-party Windows binary.

Download One File With cURL or Wget

For a single file, both tools are appropriate.

cURL download

bash
  • --fail makes HTTP 400-and-higher responses return an error instead of silently becoming the output file.
  • --location follows redirects.
  • --output selects the local filename.

Without --output or --remote-name, cURL writes the response body to the terminal. Use --remote-name when the URL's final path segment is the desired filename:

bash

Wget download

bash

Wget saves to a file by default, so the shorter form is often enough:

bash

Use an explicit destination in automation so reruns and downstream paths are predictable. Check the exit status and validate the resulting size, content type, or checksum rather than assuming that the presence of a file proves success.

cURL vs Wget for API Requests

cURL is usually the better API client because it makes headers, methods, bodies, output, and diagnostics easy to compose.

GET JSON with cURL

bash

This combination is useful in scripts:

  • --silent removes the progress meter.
  • --show-error keeps error messages.
  • --fail-with-body returns a failure status for HTTP errors while retaining the response body for diagnostics.

POST JSON with cURL

bash

--json was added in cURL 7.82.0. It sends the supplied data and sets JSON-oriented request headers. On an older version, use explicit headers and --data:

bash

POST JSON with Wget

GNU Wget can send the same request:

bash

--output-document=- writes the response to standard output. This is workable for occasional requests, but cURL generally provides a clearer interface for API-heavy scripts, multipart data, request tracing, and fine-grained transfer controls.

cURL vs Wget for Uploads

cURL is the straightforward choice when an API expects a file upload.

Multipart form upload with cURL

bash

cURL constructs the multipart/form-data request, including boundaries and file metadata.

Raw PUT upload with cURL

bash

The server must support that method and authorize the operation.

Raw request body with Wget

GNU Wget can send a file as a raw body:

bash

However, the GNU Wget manual states that these body options do not provide multipart form upload. If the endpoint requires multipart/form-data, use cURL or an application library that implements the expected request format.

Resume an Interrupted Download

Both tools can request the remaining bytes of a partially downloaded file.

Resume with cURL

bash

--continue-at - tells cURL to inspect the local output file and resume from its current size.

Resume with Wget

bash

Continuation depends on the server supporting byte-range requests and the remote file remaining compatible with the partial local copy. If the remote object changed, appending new bytes to old content can corrupt the result. For important downloads, validate a published checksum or another trusted integrity value after completion.

In our controlled test, both commands reconstructed the same 212,992-byte file from an initial 32,768-byte partial download.

Configure Retries Carefully

Retries should be bounded and limited to failures that are safe to repeat. A repeated GET download is usually different from repeating an order-creation POST.

cURL retries

bash

The cURL manual documents the transient failures handled by --retry and its treatment of Retry-After. Do not add --retry-all-errors by habit: the official cURL manual calls it a broad option and warns about duplicate data and redirected input/output.

Wget retries

bash

--tries controls attempts per URL. --retry-on-http-error makes the selected HTTP statuses retryable; verify that the installed Wget version supports it.

For either tool:

  1. Set an attempt limit.
  2. Set connection and total-time limits appropriate to the job.
  3. Respect Retry-After and service-specific rate limits.
  4. Retry only operations that are idempotent or protected by an idempotency key.
  5. Log the final failure instead of silently treating a partial download as complete.

The controlled test returned HTTP 503 twice. Both tools succeeded on attempt 3 with the explicit retry settings shown by the test fixture.

Download a List of URLs

Wget has a direct input-file workflow. Put one authorized URL per line in urls.txt, then run:

bash

cURL can accept multiple URLs and supports parallel transfers in current versions, but output naming needs deliberate configuration:

bash

Confirm that your installed cURL supports --parallel. Bound concurrency so the client does not overwhelm the destination or saturate the local connection.

Recursively Download or Mirror a Website With Wget

The cURL command-line tool does not parse documents and follow their links. The official cURL FAQ explicitly says recursive fetching is not built in.

GNU Wget can parse HTML and CSS, follow permitted references, fetch page requisites, and convert links for local viewing. A controlled mirror command might look like this:

bash

The important controls are:

  • --mirror enables recursion and timestamping behavior suitable for a mirror.
  • --level=2 overrides the otherwise broad mirror depth with a deliberate limit.
  • --no-parent prevents traversal above the starting directory.
  • --domains restricts followed hosts.
  • --page-requisites fetches resources needed to render pages locally.
  • --convert-links rewrites downloaded links for offline use.
  • --wait, --random-wait, and --limit-rate reduce load.

Recursive retrieval can expand far beyond the initial page. Use it only for a site you own or are authorized to archive, respect applicable terms and rights, retain Wget's default robots behavior, and test with a low depth first. A local copy is not permission to republish copyrighted material.

Our controlled mirror collected three linked HTML pages, a stylesheet, a referenced asset, and robots.txt. It did not establish that the same command is suitable for every real site.

Authentication With cURL and Wget

Both tools support HTTP Basic authentication.

Basic authentication with cURL

bash

Basic authentication with Wget

bash

Bearer tokens can be sent as a header:

bash

The literal username/password forms above are placeholders. Production secrets should not be committed to source code or left in shared shell history, process listings, screenshots, or debug logs. Prefer a protected .netrc or tool configuration file, a secret manager, or your operating environment's approved credential mechanism, and restrict file permissions.

Use cURL or Wget With a Proxy

cURL proxy command

bash

cURL can also use SOCKS proxies. Use socks5h:// when the proxy should resolve the destination hostname:

bash

Wget proxy command

GNU Wget normally reads HTTP, HTTPS, and FTP proxy settings from lowercase environment variables:

bash

Use no_proxy for destinations that should connect directly. Be careful with inherited proxy environment variables in CI systems: a command may use a proxy even when it is not visible in the command itself.

The proxy test for this article used a controlled authenticated HTTP proxy. Both tools reached the origin through it, and both rejected an intentionally wrong proxy password. No successful direct fallback was observed during those failed-authentication checks. That validates the tested syntax and failure path, not every proxy protocol or provider.

With Proxidize, use the hostname, port, protocol, username, and password generated for the relevant access point. Residential proxies are the usual fit for global location-specific requests, while mobile proxies are designed around real US mobile-network exits. The proxy manages the route; cURL or Wget still manages the request or download. For deeper cURL coverage, read How to Use cURL With a Proxy.

cURL or Wget: Which Should You Choose?

Your taskStart withWhy
Call a JSON REST APIcURLConcise methods, headers, JSON bodies, authentication, and error output
Upload a multipart formcURLNative `--form` support
Send a raw PUT bodycURLDirect upload controls; Wget can also do this when needed
Download one named fileEitherBoth support output naming, errors, redirects, and continuation
Resume a large interrupted downloadEitherBoth support byte-range continuation when the server does
Download a file list unattendedWgetInput-file and retrieval-oriented defaults
Mirror a permitted websiteWgetBuilt-in recursive traversal, requisites, timestamping, and link conversion
Inspect request and connection detailscURLRich verbose and trace controls
Use a SOCKS5 proxycURLDirect SOCKS configuration, including proxy-side DNS with `socks5h://`
Embed transfer behavior in an applicationlibcurl or an HTTP libraryShelling out to either CLI is often less maintainable than a library API

For a long-running application, neither command-line tool may be the final architecture. An HTTP library gives the program structured responses, cancellation, metrics, connection pooling, tests, and explicit secret handling. Use the CLI tools for shell automation, diagnostics, controlled transfers, and jobs where their process model is appropriate.

Common cURL and Wget Mistakes

Saving an HTTP error page as the requested file

cURL does not treat HTTP 404 or 500 as a command failure unless a fail option is enabled. Use --fail for downloads or --fail-with-body when an API error body is useful. Wget reports server errors through its exit status, but scripts must still check that status.

Forgetting cURL redirect handling

Add --location when the expected download URL redirects. Review authentication behavior across hosts before forwarding sensitive credentials.

Retrying state-changing requests blindly

Repeating a POST or PUT can create duplicate effects. Use idempotency support or application-specific recovery instead of assuming that a network error means nothing happened.

Resuming into the wrong local file

Continuation appends based on local size. Confirm that the local partial file belongs to the same remote object and validate integrity after completion.

Running an unbounded recursive download

--mirror is intentionally broad. Restrict depth, domain, and directory; add delays; estimate storage; and monitor the first run.

Exposing credentials

Command-line arguments, environment variables, config files, verbose logs, and process inspection each have different exposure risks. Use the secret-storage mechanism approved for the operating environment and redact diagnostics before sharing them.

Disabling TLS verification as a permanent fix

Do not make curl --insecure or Wget's certificate-bypass option a routine workaround. Fix trust configuration, hostname mismatches, missing certificate chains, or proxy CA trust instead.

Assuming a proxy fixes the application workflow

A proxy cannot repair a malformed request, failed authentication, wrong URL, absent file, or unauthorized recursive crawl. Verify the direct workflow first when practical, then add and test the network route.

Final Verdict

cURL is the better default for APIs, custom HTTP requests, uploads, debugging, protocol flexibility, and explicit per-command proxy control. GNU Wget is the better default for file-oriented automation, URL lists, continuation, recursive retrieval, and controlled website mirrors.

There is substantial overlap. The useful distinction is not that cURL transfers data while Wget downloads data—both do both. It is that cURL exposes the request as the main object, while Wget treats retrieval and traversal as the main job.

Frequently asked questions

cURL focuses on configurable data transfer and request control. Wget focuses on retrieving resources to disk and recursively following permitted links. cURL is usually better for APIs; Wget is usually better for mirrors and unattended file retrieval.

Only for some tasks. cURL is better for APIs, custom headers, uploads, debugging, and SOCKS proxies. Wget is better for built-in recursion, mirroring, and retrieval-oriented batch jobs.

Yes. Use --output filename to choose a local name or --remote-name to use the URL's filename. Add --location for redirects and --fail so HTTP error responses do not become apparently successful files.

Yes. GNU Wget can set methods and headers and send body data or a body file. It is less convenient than cURL for complex API workflows, and classic Wget does not provide cURL-style multipart form uploads.

No. The cURL command-line tool does not contain a recursive crawler. Use Wget for a controlled mirror or use a crawler designed for the site's rendering and data model.

Both can work. Use curl --continue-at - or wget --continue. Successful resumption depends on server byte-range support and an unchanged remote object; verify the completed file's integrity.

cURL uses options such as --retry, --retry-delay, and --retry-max-time. Wget uses --tries, --waitretry, and, in supported versions, --retry-on-http-error. Keep retries bounded and do not repeat non-idempotent operations without safeguards.

Yes. cURL supports explicit per-command proxy options and several proxy schemes depending on its build. GNU Wget uses proxy environment variables or configuration, with separate proxy credentials when needed.

GNU Wget's recursive retrieval respects robots directives by default. That does not by itself establish permission, copyright rights, or legal compliance; confirm the site's terms and your authority before creating a mirror.

It depends on the operating system and image. Check with curl --version and wget --version. Current Windows normally includes curl.exe, macOS includes a cURL build, and GNU Wget often requires separate installation. Minimal Linux images may include neither.

Ready to launch?

Proxies built for real operations.

For teams that depend on stability, not luck.