/commands/curl/curl-download-file
curl -O / curl -o file: download a file with curl
- tool
- curl
- versions
- curl 7.x, curl 8.x
- os
- linux, macos, windows
curl -O https://example.com/file.zip downloads the file and saves it in the current directory under the name from the URL (file.zip). curl -o name.zip URL saves it under a name you choose. Add -L to follow redirects, which most download links use, and -f so that an HTTP error such as 404 fails with a non-zero exit status instead of saving the error page. The usual safe form is curl -fLO URL.
Example
A small Node.js server on 127.0.0.1 stands in for a download site; it serves one file, sends a Content-Disposition file name, answers 404 for anything else, and stops when the block requests /quit:
cat > server.js <<'EOF'
const s = require("http").createServer((req, res) => {
if (req.url === "/quit") { res.end(); return process.exit(0); }
if (req.url === "/files/report.csv") {
res.writeHead(200, { "Content-Type": "text/csv", "Content-Disposition": 'attachment; filename="q3.csv"' });
return res.end("id,total\n1,10\n");
}
res.writeHead(404, { "Content-Type": "text/plain" });
res.end("not found\n");
});
s.listen(5140, "127.0.0.1");
setTimeout(() => process.exit(0), 20000);
EOF
node server.js &
sleep 1
U=http://127.0.0.1:5140
curl -s -O "$U/files/report.csv"
curl -s -o data.csv "$U/files/report.csv"
curl -s -OJ "$U/files/report.csv"
curl -s --create-dirs -o downloads/report.csv "$U/files/report.csv"
ls *.csv downloads
cat data.csv
curl -s -O "$U/missing.zip"; echo "without -f: exit $?, saved: $(cat missing.zip)"
curl -sf -o other.zip "$U/missing.zip"; echo "with -f: exit $?"
curl -s "$U/quit"Output
data.csv q3.csv report.csv downloads: report.csv id,total 1,10 without -f: exit 0, saved: not found with -f: exit 22
-O used the last part of the URL, report.csv. -o chose the name. -J together with -O took the name from the Content-Disposition header, q3.csv. --create-dirs created downloads/. The last two commands show why -f matters: without it the 404 page is saved as missing.zip and curl exits 0; with it curl exits 22 and saves nothing.
Options
| Option | Effect |
|---|---|
| -O, --remote-name | Save under the file name from the URL, in the current directory |
| -o FILE, --output FILE | Save as FILE; -o - writes to standard output |
| -J, --remote-header-name | With -O, use the name from Content-Disposition (never overwrites an existing file) |
| -L, --location | Follow redirects |
| -f, --fail | Exit 22 on HTTP 400 and above, save nothing |
| -C - | Resume a partial download |
| --create-dirs | Create the directories in the -o path |
| --output-dir DIR | Save -O downloads in DIR (curl 7.73+) |
| -#, --progress-bar | Simple progress bar instead of the progress table |
| -s -S | Silent, but still print errors |
| --retry 3 | Retry transient failures: timeouts and HTTP 408, 429, 500, 502, 503 and 504 |
Pitfalls
- URLs with query strings. With
-O,https://host/get?id=7is saved asget(curl 8.21 drops the query part); use-oto choose a proper name. - Unquoted URLs.
&,?and[have meanings in the shell and in curl's URL globbing. Quote the URL, and use-gif it contains literal brackets. - Overwriting.
-oand-Ooverwrite an existing file without asking. Only-Jrefuses to overwrite: curl 8.21 printsWarning: Failed to open the file q3.csv: File existsand exits with code 23. - Windows. In PowerShell 5.1
curlis an alias ofInvoke-WebRequest; callcurl.exeto get real curl, which ships with Windows 10 and later.
Confirm the download
Check the exit status (echo $?, 0 means success), then the file: ls -l file.zip, file file.zip, and if the site publishes a checksum, sha256sum file.zip. -w '%{http_code} %{size_download}\n' prints the status code and the number of bytes received after the transfer.
FAQ
Why did curl save an HTML page instead of my file?
The URL redirected, or the server returned an error page. Without -L curl saves the redirect response body, and without -f it saves a 404 or 403 page with exit status 0. Use curl -fLO URL, and check the saved file with file or head.
How do I resume a download that stopped?
Run the same command with -C -, as in curl -C - -O URL. curl looks at the size of the partial file and asks the server for the rest; the server must support range requests.
How do I download several files at once?
Repeat -O for each URL, curl -O URL1 -O URL2, or use -O with a URL pattern such as https://host/file[1-5].txt. curl 7.66 and later can fetch them in parallel with -Z.