To download a file with curl, add -O to keep the file name from the URL, or -o and a name of your own. Add -L so curl follows redirects, and -f so it does not save an error page as your file. If the download breaks, run the same command with -C - added, and curl continues where it stopped.
curl -L -f -O https://example.com/file.zip
We ran every command below on 10 October 2026. Most ran with curl 8.5.0, the version Ubuntu 24.04 ships, and some with curl 8.21.0, the copy in Windows 11. Our test file was a 165,028-byte PNG on our own site. The two versions behave differently in three places, and this page shows where.
How do you download a file with curl?
You need curl, which Windows 10 and 11, macOS and most Linux systems include. What is cURL? shows how to check your version, and the version matters here more than usual.
Step 1: choose the name with -o
curl -o tracker.png https://hproxy.com/blog/_assets/img/amazon-price-tracker-check-output.png
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 161k 100 161k 0 0 269k 0 --:--:-- --:--:-- --:--:-- 269k
-o writes the body to the file you name. The meter shows 161k for 165,028 bytes, because curl counts 1k as 1,024 bytes.
Step 2: keep the name from the URL with -O
curl -O https://hproxy.com/blog/_assets/img/amazon-price-tracker-check-output.png
-O saved the file as amazon-price-tracker-check-output.png, the last part of the URL. Three details came out of our runs:
- The query string is dropped.
robots.txt?v=2was saved asrobots.txt. - A file of the same name is overwritten without a question.
- A URL that ends in a slash has no name to take. curl 8.5.0 stopped with exit 23 and
Failed writing received data to disk/application. curl 8.21.0 saved the same page ascurl_response, the fallback the manual gives for 8.10.0 and newer.
Step 3: let the server choose the name with -J
curl -OJ 'https://httpbin.org/response-headers?Content-Disposition=attachment%3B%20filename%3Dreport.csv'
A server can suggest a name in its Content-Disposition header, which RFC 6266 defines. -J tells -O to use it, and our run saved report.csv. curl does not overwrite an existing file this way unless you add --clobber. The manual also warns that a hostile server could pick a dangerous name, such as a DLL on Windows.
Redirects change the name as well. After a redirect, -O still names the file after the first URL. Our download through httpbin's redirect-to address was saved as redirect-to on both builds. With -OJ, curl 8.21.0 named it after the target, llms.txt, while curl 8.5.0 still wrote redirect-to. The manual dates that fallback to curl 8.19.0.
Step 4: save into a folder
curl --create-dirs --output-dir downloads/2026-10 -O https://hproxy.com/robots.txt
--output-dir (curl 7.73.0 and newer) puts the file in that folder. It fails when the folder does not exist, so --create-dirs creates it. Our run made downloads/2026-10 and saved the 910-byte file inside.
Step 5: download several files
Give every URL its own -O, or put --remote-name-all in front to apply -O to all of them. -Z (--parallel, from curl 7.66.0) fetches them at the same time, 50 at most by default.
curl -# -O https://hproxy.com/robots.txt -O https://hproxy.com/llms.txt
-# swaps the meter for a simple bar. Our two files came without a stated size, so the bar showed only #=#=# for each.

How do you know the file is the one you wanted?
Exit code 0 means curl finished the transfer, not that you got the file. Two failures in our run exited 0 and still left a file behind.

A redirect page saved as your file
Our site sends http:// visitors to https:// with a 301. Without -L, curl saved that answer under the name robots.txt:
$ curl -sO http://hproxy.com/robots.txt; head -c 75 robots.txt
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center>
With -L, the same command saved the real 910-byte file. Following redirects with curl covers the limits and what happens to logins.
An error page saved as your file
A missing file gave a 404, and curl saved the error page as report-2026.zip with exit code 0. The file command found an HTML document inside. With -f, curl stopped with exit 22 and wrote no file:
$ curl -sSfO https://hproxy.com/report-2027.zip; ls -l report-2027.zip
curl: (22) The requested URL returned error: 404
ls: cannot access 'report-2027.zip': No such file or directory
If you want the error page and the exit code, use --fail-with-body, from curl 7.76.0.
Check the size, a hash or a signature
After the download, compare the file with what the publisher states. -w '%{http_code} %{size_download}' makes curl print the status and the byte count. sha256sum on Linux, or Get-FileHash in PowerShell, prints a SHA-256 hash to compare with a published one.
curl's own releases come with a signature instead. We checked curl 8.22.0 the way curl's verify page describes, with the release key from the link on that page:
curl -sSfLO https://curl.se/download/curl-8.22.0.tar.xz
curl -sSfLO https://curl.se/download/curl-8.22.0.tar.xz.asc
curl -sSf -o curl-key.asc 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x27edeaf22f3abceb50db9a125cc908fdb71e12c2'
gpg --import curl-key.asc
gpg --verify curl-8.22.0.tar.xz.asc curl-8.22.0.tar.xz
gpg: Signature made Wed Sep 2 07:48:56 2026 CEST
gpg: using RSA key 27EDEAF22F3ABCEB50DB9A125CC908FDB71E12C2
gpg: Good signature from "Daniel Stenberg <daniel@haxx.se>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 27ED EAF2 2F3A BCEB 50DB 9A12 5CC9 08FD B71E 12C2
We gave each gpg command --homedir ./gpg, a throwaway keyring; your own gpg needs no extra option. The fingerprint is the one curl's verify page lists. The warning means gpg itself cannot vouch for the key. Comparing the fingerprint with the publisher's own page is the step that does.

How do you resume a broken download?
Add -C - to the same command. The dash tells curl to read the size of the partial file and ask the server for the rest.
curl -C - -o part.png https://hproxy.com/blog/_assets/img/amazon-price-tracker-check-output.png
In our run, a download cut after 3 seconds stopped at 89,337 of 165,028 bytes, with exit 28. The resume printed ** Resuming transfer from byte position 89337 and fetched the other 75,691 bytes. sha256sum gave the same hash for the resumed file and for a straight download.
Running -C - on a file that is already complete does no harm. curl reported byte position 165,028, transferred nothing and exited 0.
Resuming needs the server's help. RFC 9110 lets a server ignore a Range request, and curl's manual notes that many servers then send the whole file. Our server announces accept-ranges: bytes for the test file, so it resumed.
Does --retry resume a broken download?
That depends on your curl version. Several guides pair -C - with --retry as if it always resumes. We cut the same download every 3 seconds and let --retry 3 try again:
| curl version | What we ran | What happened |
|---|---|---|
| 8.5.0, Ubuntu 24.04 | --retry 3 | Four attempts, each from byte 0; exit 28; 80,158 bytes kept |
| 8.5.0, Ubuntu 24.04 | --retry 3 -C - | Four attempts, each from byte 0; exit 28; 53,212 bytes kept |
| 8.21.0, Windows 11 | --retry 3 -C - | The second attempt asked for the last 95,050 bytes; exit 0; whole file, same hash |
curl's changelog explains the difference. Version 8.17.0, released on 5 November 2025, made curl keep a failed partial download for retry auto-resume. Ubuntu 24.04 ships 8.5.0, so its retries start over.
On an older curl, a shell loop does the same job, because each new run of curl -C - continues from the partial file. We added --limit-rate 20k -m 3 to cut each run after 3 seconds:
until curl --no-progress-meter --limit-rate 20k -m 3 -C - -o loop.png https://hproxy.com/blog/_assets/img/amazon-price-tracker-check-output.png; do sleep 1; done
On curl 8.5.0, the loop resumed at byte 86,477, finished on its third run and produced the right hash.

More retry rules from the manual: --retry is off by default. It covers timeouts and the HTTP codes 408, 429, 500, 502, 503 and 504, as the manual of curl 8.5.0 lists them; the current manual adds 522 and 524. curl waits 1 second, then doubles the wait up to 10 minutes, and --retry-delay sets a fixed wait instead. --retry-all-errors retries any error, and the manual calls it the sledgehammer of retrying.
How do you limit the download speed?
--limit-rate caps the speed in bytes per second, with k and M counted in steps of 1,024. The cap is an average over several seconds, so a short download can beat it. Our 20k cap allowed 26,847 and 29,429 bytes per second in two downloads cut at 3 seconds. --max-filesize refuses a file above a size you set, with exit 63.
How do you download a file through a proxy?
A download takes a proxy like any other curl request: -x for the proxy and -U for its login.
curl -x http://premium.hproxy.com:10000 -U USER:PASS -O https://example.com/file.zip
We tested this against a stub proxy on our server that asked for the login USER:PASS. The lower half of the capture above shows the run:
| What we ran | Result | What the proxy saw |
|---|---|---|
-x http://127.0.0.1:18081 -U USER:PASS -O on robots.txt | Status 200, 910 bytes saved | CONNECT hproxy.com:443 with the login header |
-x socks5h://USER:PASS@127.0.0.1:11081 on the PNG | Status 200, 165,028 bytes saved | The name hproxy.com, looked up by the proxy |
-x http://127.0.0.1:18081 -O on llms.txt, no login | Exit 56, CONNECT tunnel failed, response 407 | The request, refused; no file was created |
A refused proxy leaves no file behind, unlike an error page from the site itself. How to use proxies with curl covers the proxy schemes and environment variables. The cURL proxy error guide decodes the exit codes. Some downloads must come from another country. For those, our residential proxies take the same -x and -U flags, with the host, port and login from your dashboard.
How do you download a file on Windows?
Windows 10 and 11 include curl.exe, and the options on this page work the same in it. In PowerShell, three details matter:
- In Windows PowerShell 5.1, type
curl.exe, becausecurlalone runs Invoke-WebRequest. What is cURL? shows the errors that follow. - Write
-o nulwhere Linux uses-o /dev/null. - Save with
-o, never with>.
We downloaded our PNG three ways on Windows 11, with the curl.exe it ships:
| How we saved it | Bytes | First bytes | Same as the original? |
|---|---|---|---|
curl.exe -o ok.png URL | 165,028 | 89 50 4e 47 | Yes |
curl.exe URL > ps51.png in Windows PowerShell 5.1 | 313,602 | ff fe fd ff | No, the image is broken |
curl.exe URL > ps7.png in PowerShell 7.6.6 | 165,028 | 89 50 4e 47 | Yes |
Windows PowerShell 5.1 sends > through Out-File, which writes UTF-16 text by default, so a binary file comes out broken. PowerShell 7.4 changed redirection to keep the bytes of programs like curl. -o works the same in every shell.
What goes wrong most often?
| What you see | Why | What to do |
|---|---|---|
The file holds 301 Moved Permanently | curl saved the redirect page | Add -L |
| The file is an HTML error page, and the exit code is 0 | curl saved the error page | Add -f |
curl: (23) Failed writing received data to disk/application with -O | The URL ends in a slash, on curl older than 8.10.0 | Name the file with -o |
The file is called redirect-to | -O took the name of the first URL | Use -o, or -OJ on curl 8.19.0 or newer |
--retry starts again from zero | curl older than 8.17.0 | Loop curl -C - until it exits 0 |
A broken image or archive after > in PowerShell 5.1 | Out-File wrote UTF-16 text | Save with -o |
curl: (56) CONNECT tunnel failed, response 407 | The proxy wants a login | Add -U |
curl: (28) Operation timed out | -m ran out, or the network is slow | Raise -m, then resume with -C - |
What this page could not check
We ran two curl builds: 8.5.0 on Ubuntu 24.04 and 8.21.0 on Windows 11. macOS and other Linux distributions were not run. We cut downloads with --max-time instead of a real network drop. Resuming was tested on our own server, which honours ranges, and a server that ignores Range was not tested. The signature check rests on the key that curl's site links, and gpg cannot make that call for you. The proxy runs used a stub on our server, not a commercial network. Microsoft's redirection page for PowerShell 5.1 says it writes UTF-8, but its Out-File page and our test both point to UTF-16. curl releases about every eight weeks, so we will check the version notes on this page again by 10 January 2027.
Sources
- curl manual for download options (the man page of version 8.23.0), read 10 October 2026: curl.se/docs/manpage.html.
- curl project, changelog, with 8.17.0 of 5 November 2025, read 10 October 2026: curl.se/changes.html.
- curl project, download page and verify page, read 10 October 2026: curl.se/docs/verify.html.
- RFC 9110, HTTP Semantics, June 2022, for range requests, 301 and 416: rfc-editor.org/rfc/rfc9110.
- RFC 6266, Content-Disposition in HTTP, June 2011: rfc-editor.org/rfc/rfc6266.
- GnuPG manual, Operational GPG Commands, read 10 October 2026: gnupg.org.
- Microsoft Learn, Out-File and about_Redirection (PowerShell 5.1), updated in July and September 2023: learn.microsoft.com.
- Microsoft Learn, What's New in PowerShell 7.4, updated 8 September 2026: learn.microsoft.com.
- Microsoft Learn, Get-FileHash, updated 12 December 2022: learn.microsoft.com.
- Our own downloads of 10 October 2026, with curl 8.5.0 on Ubuntu 24.04 and curl.exe 8.21.0 on Windows 11. They fetched from our own site, httpbin.org and curl.se, partly through a stub proxy we wrote. Every transcript, capture and script sits in this page's research folder.


