curl and wget Basics: HTTP Communication from the Command Line
What you'll be able to do
- Pick `curl` or `wget` based on what you actually need to do
- Check where a download lands and whether it will overwrite something
- Follow redirects, inspect headers, and POST JSON from copy-paste examples
Prerequisites (read these first)
What You'll Learn
- The role difference between
curlandwget - Where a downloaded file lands before you download it
- How to avoid overwriting a file you still need
- How to inspect headers and call APIs from copy-paste commands
Words used here (sorted out first)
- Download: copying data from a machine on the network onto your own machine.
- Redirect: a note from the server that says "this page moved elsewhere." This is not the shellAn interactive program that reads the commands you type and runs them.'s
>redirection. One word, two meanings. - Header: a label sent alongside the body. It states the type and the size of the content.
- Status code: a three-digit number for the result.
200means success,404means not found. - API: a doorway that programs use to talk to each other. The reply is usually JSON, not HTML.
Quick Summary
- One-shot file download →
wget URL - Hit an API, inspect headers, or POST →
curl URL - Want to follow redirects →
curl -L URL - Download was interrupted →
wget -c URL
Environment
- OS: Ubuntu / typical Linux
curlis usually preinstalled. Installwgetviasudo apt install wget- We focus on HTTP/HTTPS (curl also supports ftp, sftp, smtp, etc.)
1. curl vs wget: Different Roles
Conclusion: wget is a downloader that saves by default; curl is an HTTP client that prints.
At a glance
| Aspect | curl | wget |
|---|---|---|
| Default behavior | Prints to stdout | Saves as a file |
| HTTP methods | GET/POST/PUT/DELETE/... | Mostly GET |
| Follow redirects | Need -L flag |
Automatic |
| Recursive download | Weak | Strong (-r) |
| Resume | -C - |
-c |
| Best for | APIs, debugging, POST | Mirroring sites, large DL |
Rule of thumb: APIs, headers, or POST involved → curl. Just grabbing a file → wget.
2. curl Basics: Print Before You Save
Conclusion: curl prints to the screen by default; add -o name or -O to save to a file.
2-1. Hit a URL and Print the Response
$ curl https://example.com
<!doctype html>
<html>
<head>
<title>Example Domain</title>
...
-o (specify name) or -O (use the URL's filename).2-2. Save With a Custom Name: -o
$ curl -o page.html https://example.com
-o stands for output. Lowercase o takes the name you want as its argument.
2-3. Save Using the URL's Filename: -O
$ curl -O https://example.com/sample.tar.gz
Uppercase O reuses the filename at the end of the URL. Here that is sample.tar.gz.
Mixing up -o and -O ends in tears
-Orequires the URL to end with a real filenamecurl -O https://example.com/(trailing slash) fails — there is no filename to use- When in doubt, use
-o nameand be explicit
2-4. Where Does It Land? Will It Overwrite?
Check two things before any download: where the file is created, and what happens if that name already exists.
Save location and overwriting, up front
- The file lands in the directoryA container that organizes files. Same idea as a "folder" on Windows or macOS. you are in right now. Check it with
pwd curl -Oandcurl -ooverwrite a file of the same name without asking- Unlike a GUI file manager, there is no confirmation dialog. The old content is gone
- To block overwriting outright, use
curl --no-clobber -O URL. If the name is taken, curl does nothing wgetdoes not overwrite by default. It savessample.tar.gz.1with a number appended insteadcurlandwgetdo not run in this site's virtual terminal. Run them in your own terminal- Practice inside the empty directory you create in "10. Mini Exercises". Nothing you already had can break there
curl -O and edited it. Then I ran the same command again. My edits were gone.ls before you download to see whether the name is taken. If you want a different name, choose it yourself with -o. Those two habits prevent almost every accident.ls before downloading" would have saved me.# Before downloading, check where you are and what is already there $ pwd $ ls sample.tar.gz # When overwriting worries you, pick the name yourself $ curl -o sample-20260605.tar.gz https://example.com/sample.tar.gz
2-5. Show Download Progress
$ curl -O --progress-bar https://example.com/big.iso
--progress-bar gives a clean progress bar. Long transfers stay readable.
3. The #1 Beginner Trap: Redirects and -L
Conclusion: curl does not follow redirects by default; add -L to reach the final page.
https://github.com/torvalds/linux and the response looks wrong.-L and curl follows it.3-1. Follow Redirects With -L
# BAD: only the redirect placeholder is returned $ curl https://github.com/torvalds/linux # GOOD: fetch the final destination $ curl -L https://github.com/torvalds/linux
-L stands for Location. Location is the HTTP header that carries the new address.
Memorize the pattern
- When hitting external sites with curl, always add
-Lto avoid surprises - File downloads often require
-Ltoo (GitHub Releases, S3 redirects, etc.)
# Standard safe pattern $ curl -LO https://github.com/some/repo/releases/download/v1.0/binary.tar.gz
-LO is -L and -O joined together. Single-letter options can be combined like this. Writing -L -O means the same thing.
4. Inspect Headers Only With -I
Conclusion: -I fetches headers only without the body; the status code reveals the state.
Want to check "is this link alive?" or "where does this redirect to?" without downloading the body? Use -I.
$ curl -I https://example.com
HTTP/2 200 content-type: text/html; charset=UTF-8 content-length: 1256 date: Sun, 26 May 2026 09:00:00 GMT server: ECS
HTTP/2 200 means "success," right?HTTP status cheat sheet
| Code | Meaning | Examples |
|---|---|---|
2xx |
Success | 200 OK, 204 No Content |
3xx |
Redirect | 301 (permanent), 302 (temp) |
4xx |
Client-side error | 404 (not found), 401/403 |
5xx |
Server-side error | 500 (bug), 503 (overload) |
4-1. Trace Where a Redirect Leads
$ curl -ILs https://bit.ly/3xxxxx | grep -i location
-ILs is -I, -L, and -s combined into one. -L follows redirects, -s silences progress and errors, -I shows headers only. Each hop adds its own location: line, and the last one is the final destination.
5. POST and JSON: Talking to APIs
Conclusion: Hit APIs with three flags: -X for method, -H for headers, -d for the body.
-X for the HTTP method, -H for headers, and -d for the body. Remember those three flags and you're done.5-1. GET With Query Parameters
$ curl "https://api.example.com/users?id=42"
Always quoteWrapping a string in quote marks (' or ") so it is treated as one single value, e.g. one with spaces in it. URLs containing ? and &. Without quotes the shell reads & as background-execution.
5-2. POST JSON
$ curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"lina","role":"beginner"}'The three-flag combo
-X POST: the HTTP method.-dalready makes curl send a POST, so you usually do not need to write it-H "Content-Type: application/json": declare "this is JSON"-d '...': the request body. Use single quotes so the inner"need no escaping
5-3. Bearer Token Authentication
$ curl https://api.example.com/me \
-H "Authorization: Bearer YOUR_TOKEN_HERE"Keep tokens out of your shell history
# BAD: visible in `history` and `ps` $ curl -H "Authorization: Bearer abc123..." ... # GOOD: pass via environment variable $ export API_TOKEN=abc123... $ curl -H "Authorization: Bearer $API_TOKEN" ...
Tokens visible in history or ps are an incident waiting to happen. Use env vars or ~/.netrc.
5-4. Basic Auth
$ curl -u username:password https://example.com/private
-u accepts user:pass. HTTPS only.
6. wget's Strength: Reliable Downloads
Conclusion: wget saves by default and follows redirects; -O renames, -c resumes a download.
6-1. Basic: Save as a File
$ wget https://example.com/sample.tar.gz
wget does not overwrite on its own
If the name is already taken, wget saves sample.tar.gz.1 — the same name with a number appended. The exception is -O name: that form does overwrite the name you gave. Keep that difference in mind.
6-2. Save With a Custom Name: -O
$ wget -O custom.tar.gz https://example.com/sample.tar.gz
-O means the opposite in curl and wget
- curl's
-O: save using the URL's filename - wget's
-O: specify the filename (equivalent to curl's-o)
Everyone who uses both gets bitten by this at least once.
6-3. Resume an Interrupted Download: -c
$ wget -c https://example.com/big.iso
-c stands for continue. If your connection dropped halfway, this picks up from where it stopped instead of restarting.
6-4. Retries and Timeouts
$ wget --tries=5 --timeout=30 https://example.com/file.zip
Survives flaky networks by retrying.
7. Recursive Download wget -r (Handle With Care)
Conclusion: wget -r recurses through links; always cap depth with -l and scope with -np.
$ wget -r -l 2 -np https://example.com/docs/
-r: recurse through links-l 2: cap depth at 2 levels-np: don't ascend to parent directories
Things you must not do
- Run
-rwith no limits → you may download the entire site - Hammer the server rapid-fire → it looks like a DoS attack
- Ignore
robots.txt→ violates terms of service
Add --wait=2 to space requests, scope the URL tightly, and check the site's policy first.
8. Encoding and Line-Ending Traps
Conclusion: curl and wget hand you raw bytes; fix encoding with iconv and CRLF with tr.
iconv.\r\n (CRLF). On Linux, strip them with dos2unix or tr -d '\r'.# Convert Shift_JIS to UTF-8 while saving $ curl -s https://example.com/sjis.html | iconv -f SHIFT_JIS -t UTF-8 > page.html # Strip CRLF to LF $ curl -s https://windows-server.example/data.csv | tr -d '\r' > data.csv
> here is shell redirection. It also replaces the target file's contents without asking. Do not point it at a name you still need.
9. Common Beginner Pitfalls
Conclusion: Common traps: forgetting -O/-o to save, forgetting -L, and unquoted & in URLs.
9-1. curl URL Saved Nothing
Cause: curl prints to stdout by default. To save, use -O or -o.
$ curl -O https://example.com/file.zip $ curl -o my.zip https://example.com/file.zip
9-2. Got the Redirect Page Instead of the Content
Cause: forgot -L.
$ curl -L https://github.com/...
9-3. Part of the Query Is Ignored, or an Odd Error Appears
Cause: the shell read & as the background-execution operator. Everything before & goes to curl; the rest becomes a separate command.
- With
&page=2(an=is present): no error appears.&page=2is silently dropped and only the result differs. This is the hardest case to notice - With
&page(no=): you getpage: command not found
# BAD $ curl https://api.example.com/search?q=linux&page=2 # GOOD $ curl "https://api.example.com/search?q=linux&page=2"
9-4. SSL Certificate Error
Cause: self-signed cert, expired cert, internal CA, etc.
# With verification (recommended) $ curl https://internal.example.com # Skip verification (emergencies only — never in production) $ curl -k https://internal.example.com
-k disables verification, opening the door to man-in-the-middle attacks. For production, fix the certificate chain instead.
9-5. Proxy Environment Can't Connect
$ export http_proxy=http://proxy.example.com:8080 $ export https_proxy=http://proxy.example.com:8080 $ curl https://example.com
In corporate environments, set http_proxy / https_proxy.
10. Mini Exercises
Conclusion: Use httpbin.org to practice status checks, JSON POST, and following redirects.
https://httpbin.org is a safe sandbox for HTTP testing — hit it freely.Make an empty practice directory and work inside it. That way you never overwrite a file you already had.
# Prepare a practice directory and move into it $ mkdir -p ~/curl-practice && cd ~/curl-practice
~stands for your home directory, the space that belongs to you&&means "run the right side only if the left side succeeded." Two separate lines work the same
Exercise 1: Check the HTTP status code and content-type of https://httpbin.org/get.
Show Hint 1 (Direction)
You do not need the body. There is a flag that fetches only the labels.
Show Hint 2 (Command name)
The command is curl. The flag is the single letter from "4. Inspect Headers Only".
Show Answer
$ curl -I https://httpbin.org/get
HTTP/2 200 date: ... content-type: application/json ...
The leading 200 means success. content-type: application/json says the reply is JSON.
Exercise 2: POST the JSON {"hello":"world"} to https://httpbin.org/post.
Show Hint 1 (Direction)
You need three things: the method, the "this is JSON" declaration, and the body.
Show Hint 2 (Command name)
The command is curl. The flags are the three used in "5-2. POST JSON".
Show Answer
$ curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
-d '{"hello":"world"}'You'll see "json": {"hello": "world"} echoed back in the response.
Exercise 3: Follow the redirect chain at https://httpbin.org/redirect/3 and fetch the final page.
Show Hint 1 (Direction)
curl ignores the "moved" note unless you tell it to chase the note.
Show Hint 2 (Command name)
The command is curl. The flag is the single letter from "3. The #1 Beginner Trap" — the first letter of Location.
Show Answer
$ curl -L https://httpbin.org/redirect/3
After three hops it lands on /get and returns that response.
11. Copy-Paste Templates
Conclusion: Copy-paste curl and wget templates for saving, redirects, headers, and POST.
curl templates
# Print to screen (debug)
curl URL
# Save (using URL's filename; overwrites same name)
curl -O URL
# Save (custom name; overwrites same name)
curl -o name URL
# Follow redirects and save (GitHub Releases, etc.)
curl -LO URL
# Headers only
curl -I URL
# Get just the status code
curl -s -o /dev/null -w "%{http_code}\n" URL
# POST JSON
curl -X POST URL \
-H "Content-Type: application/json" \
-d '{"key":"value"}'
# Bearer token
curl URL -H "Authorization: Bearer $API_TOKEN"
# Basic auth
curl -u user:pass URL
# With progress bar
curl -O --progress-bar URLwget templates
# Basic download (appends a number if the name is taken) wget URL # Save with custom name (overwrites that name) wget -O custom.name URL # Resume an interrupted download wget -c URL # Retries and timeout wget --tries=5 --timeout=30 URL # Quiet download (minimal log) wget -q URL # Recursive (mind the manners) wget -r -l 2 -np --wait=2 https://example.com/docs/
Looking Back
Conclusion: Pick by purpose, look before you download, and add -L on external sites.
pwd and ls before downloading, because curl -O overwrites without asking.-L when curl hits an external site. Take those three home and you're set.Three-Line Recap
Conclusion: Pick the tool by purpose, check where files land, and add -L for external sites.
- Use
curlfor APIs and headers. Usewgetwhen you only need the file - Run
pwdandlsfirst.curl -Oandcurl -ooverwrite the same name without asking - Add
-Lwhen curl hits an external site so it follows the redirect