curl Command Deep Dive
curl is often introduced as “a command for downloading something from a URL.” That description is technically true but misses why it is so useful on Linux. curl is a compact way to interact with network services directly from the shell, especially HTTP services. Once you understand how to control the request and inspect the response, it becomes useful for testing services, debugging HTTP behavior, downloading files, sending data, and automating repetitive network tasks.
What Is curl Actually Doing?
At its simplest:
curl https://example.comasks curl to retrieve the resource at that URL.
The important part is what happens underneath:
curl
│
├── DNS resolution
│
├── TCP connection
│
├── TLS negotiation
│
└── HTTP request
│
▼
Web server
│
▼
HTTP responseSo curl is an application that exercises several parts of the Linux networking stack you have already studied.
If the URL uses HTTPS, there is another layer between TCP and HTTP:
curl
↓
TCP
↓
TLS
↓
HTTPThis makes curl particularly valuable for troubleshooting because you can inspect or control each part of the request instead of treating a browser as one opaque operation.
Start With A Simple Request
Run:
curl https://example.comThe response is written to your terminal.
If the server returns HTML, you’ll see HTML.
This is different from opening the page in a browser. A browser normally takes the response and renders it into a visual document. curl simply gives you the HTTP response body.
That makes it ideal when you care about what the server actually returned.
See The HTTP Headers
A useful first step beyond the basic request is:
curl -i https://example.comThe -i option includes the response headers together with the response body.
You may see something conceptually like:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1256
...
<!doctype html>
<html>
...Now you can see that an HTTP response isn’t just the body.
It contains metadata such as:
Status
Headers
BodyThis distinction becomes extremely important when debugging web services.
Request Headers And Response Headers Are Different
There are two directions of headers.
The server sends response headers:
Server → curl
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1256The client can send request headers:
curl → Server
GET / HTTP/1.1
Host: example.com
User-Agent: ...
Accept: ...You can inspect the request that curl sends with:
curl -v https://example.comThis is one of the most useful curl options to learn.
-v: See The Conversation
Run:
curl -v https://example.comThe verbose output shows connection and protocol details.
You’ll see information corresponding to stages such as:
DNS / address selection
↓
TCP connection
↓
TLS negotiation
↓
HTTP request
↓
HTTP responseThe output contains a lot more information than the normal command because curl is exposing what it is doing.
For HTTP, lines beginning with:
>represent data sent by curl.
Lines beginning with:
<represent data received from the server.
For example:
> GET / HTTP/1.1
> Host: example.com
> User-Agent: curl/...
> Accept: */*
< HTTP/1.1 200 OK
< Content-Type: text/htmlThis is an excellent debugging view because you can distinguish:
What did I send?from:
What did the server send back?-I: Request Headers Without The Body
Sometimes you don’t care about the response body.
Use:
curl -I https://example.comThis requests headers only using an HTTP HEAD request.
You might get:
HTTP/1.1 200 OK
content-type: text/html
content-length: 1256This is useful for quickly checking things such as:
- whether a resource exists
- the returned status code
- the content type
- the reported size
- redirect behavior
- caching-related headers
There is an important difference between:
curl -iand:
curl -I-i includes the headers with the normal response body.
-I asks for headers without downloading the normal body.
HTTP Status Codes
When working with HTTP, the status code is one of the first things you should inspect.
For example:
200 OKmeans the request succeeded.
Common categories are:
2xx → success
3xx → redirection
4xx → client-side/request problem
5xx → server-side problemExamples:
200 OK
301 Moved Permanently
302 Found
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
500 Internal Server Error
502 Bad Gateway
503 Service Unavailablecurl doesn’t hide these from you. You can inspect them with:
curl -I https://example.comor:
curl -i https://example.comFor automated checks, you can also ask curl to print only the status code:
curl -s -o /dev/null -w '%{http_code}\n' https://example.comHere:
-ssuppresses normal progress output,
-o /dev/nulldiscards the response body,
and:
-wlets you print selected information about the transfer.
This pattern is useful in scripts because you can turn an HTTP request into a simple value such as:
200Download A File
One of the most common uses of curl is downloading a file.
Suppose:
https://example.com/archive.tar.gzpoints to a file.
You can write the response to a specific filename:
curl -o archive.tar.gz https://example.com/archive.tar.gzThe general pattern is:
curl -o <local-file> <URL>Without -o, curl writes the response body to standard output, which is why:
curl https://example.com/archive.tar.gzwould attempt to dump the file into your terminal.
For a URL whose final path already contains the desired filename, -O is convenient:
curl -O https://example.com/archive.tar.gzNow curl uses the remote filename:
archive.tar.gzSo the distinction is:
-o filename
You choose the local filename.
-O
Use the filename from the URL.Follow Redirects
HTTP resources frequently redirect.
For example:
http://example.com
↓
https://example.comA server may respond with:
301 Moved Permanently
Location: https://example.com/By default, curl does not necessarily follow every redirect automatically.
Use:
curl -L https://example.comThe -L option tells curl to follow redirects.
This becomes particularly important when downloading files or interacting with modern websites.
For example:
curl -L -O https://example.com/file.tar.gzmeans:
follow redirects
+
save using the remote filenameWhy Redirects Matter During Debugging
Suppose:
curl http://example.comreturns:
301 Moved Permanently
Location: https://example.com/You might initially think the site is broken because you didn’t receive the page.
But the server actually answered correctly.
The conversation is:
curl
│
│ GET http://example.com
▼
Server
│
│ 301
│ Location: https://example.com/
▼
curlWith:
curl -L http://example.comcurl follows that instruction and performs the next request.
This is a good example of why inspecting the HTTP response is often more useful than simply saying “the command didn’t work.”
Send A Specific HTTP Method
By default, curl uses GET for a normal request.
You can explicitly select a method:
curl -X GET https://example.comFor example, you may encounter APIs that use:
GET
POST
PUT
PATCH
DELETEYou can specify one with:
curl -X POST https://example.com/api/itemsHowever, don’t automatically use -X just because you know the HTTP method.
curl has options that naturally imply certain methods.
For example, sending form data with:
curl -d 'name=alice' https://example.com/api/itemscauses curl to use POST.
The important lesson is:
Use the option that represents what you’re trying to do; don’t add
-Xunless you actually need to override the method.
Send Form Data
A common HTTP pattern is sending form data.
For example:
curl -d 'username=alice&password=secret' https://example.com/loginThe server receives a POST request containing the supplied data.
You can make the request more explicit:
curl -H 'Content-Type: application/x-www-form-urlencoded' -d 'username=alice&password=secret' https://example.com/loginThe -H option adds a request header.
The important relationship is:
-H
controls request headers
-d
supplies request dataThis is enough to reproduce many simple HTTP requests from a browser or another client.
Warning
Avoid putting real passwords or API tokens directly into shell history while experimenting. Use mock credentials in examples and be aware that shell history can retain commands.
Send JSON
Modern APIs commonly accept JSON.
For example:
curl -H 'Content-Type: application/json' -d '{"name":"alice","role":"admin"}' https://example.com/api/usersNow the request contains:
Content-Type: application/jsonand the body:
{"name":"alice","role":"admin"}This is one of the most common curl patterns you’ll encounter when working with HTTP APIs.
You can combine it with verbose mode:
curl -v -H 'Content-Type: application/json' -d '{"name":"alice","role":"admin"}' https://example.com/api/usersNow you can see the request and response around the JSON operation.
Add A Custom Request Header
The basic syntax is:
curl -H 'Header-Name: value' https://example.comFor example:
curl -H 'X-Request-ID: test-123' https://example.comThis is useful for reproducing requests where an application expects a particular header.
You can also provide common HTTP headers:
curl -H 'Accept: application/json' https://example.com/api/usersNow you’re asking the server for a JSON representation if it supports one.
Headers are one of the reasons curl is so useful for debugging. You can reproduce the important parts of an HTTP request without needing the original application.
Authentication Headers
A common API pattern is bearer-token authentication:
curl -H 'Authorization: Bearer YOUR_TOKEN' https://example.com/api/usersThe important part is not the particular token.
It is the HTTP pattern:
Authorization: Bearer <token>Similarly, basic authentication can be expressed with:
curl -u alice:password https://example.com/privateThis asks curl to construct the appropriate HTTP authentication header.
Again, use fake credentials when practicing.
Cookies
curl can also work with HTTP cookies.
To send a cookie:
curl -H 'Cookie: session=abc123' https://example.com/accountThis is useful when reproducing a request from an authenticated web session.
You can also let curl store cookies:
curl -c cookies.txt https://example.com/loginand use them later:
curl -b cookies.txt https://example.com/accountThe pattern is:
-c
write cookies to a file
-b
read cookies from a fileThis is useful when a multi-step interaction depends on session cookies.
Save And Reuse A Request
Because curl commands are plain shell commands, they are easy to reproduce.
For example:
curl -H 'Accept: application/json' -H 'X-Request-ID: test-123' https://example.com/api/usersYou can run the same command repeatedly while changing one variable at a time.
That makes curl particularly useful for debugging.
Instead of:
Browser
↓
many things happening automatically
↓
something failsyou can reduce the interaction to:
curl
↓
specific request
↓
specific responseThis reduction is one of its biggest strengths.
Inspect Only The Headers First
When debugging an HTTP endpoint, a good first move is often:
curl -I https://example.comThen, if you need more detail:
curl -v https://example.comThen, if you need to reproduce the exact request:
curl -H 'Accept: application/json' https://example.com/apiThis gives you a progression:
Status / response headers
↓
Connection + request + response details
↓
Custom request reproductionYou don’t always need the most verbose command immediately.
Debug A Connection With -v
Suppose:
curl https://example.comfails.
Try:
curl -v https://example.comNow you can ask several separate questions.
Did DNS resolve the hostname?
* Host example.com:443 was resolved.Did TCP connect?
* Connected to example.com (...)Did TLS negotiation begin and succeed?
* SSL connection using ...Was an HTTP request sent?
> GET / HTTP/1.1Did the server respond?
< HTTP/1.1 200 OKThis is much more informative than simply seeing an error message.
-s, -S, And -sS
When using curl in scripts, you often don’t want the normal progress information.
Use:
curl -s https://example.com-s means silent.
But completely silent mode can also hide useful error information.
A common combination is:
curl -sS https://example.comThis means roughly:
-s
hide normal progress output
-S
still show errorsThis is a very common scripting pattern.
Fail On HTTP Errors
There is an important distinction between:
curl successfully connected to the serverand:
the HTTP request succeededFor example:
curl https://example.com/not-foundmay receive:
404 Not FoundFrom the HTTP perspective, the server successfully returned a response.
For scripting, you may instead want curl to treat HTTP 4xx/5xx responses as failures.
Use:
curl --fail https://example.com/not-foundThe short option is:
curl -f https://example.com/not-foundThis distinction matters when writing scripts:
Network request completed
≠
HTTP operation succeededA script that only checks whether curl could connect may accidentally treat an HTTP 404 or 500 as success.
Set A Timeout
A command that can wait forever is inconvenient in automation.
You can set a maximum time for the operation:
curl --max-time 10 https://example.comNow the operation is limited to 10 seconds.
You can also set a connection timeout:
curl --connect-timeout 5 https://example.comThis focuses specifically on how long curl should wait while establishing the connection.
The distinction is useful:
--connect-timeout
How long to wait for connection establishment.
--max-time
Maximum time for the entire operation.For a script, a pattern such as:
curl -fsS --connect-timeout 5 --max-time 15 https://example.comis much safer than allowing a network problem to leave the script waiting indefinitely.
Download Files Reliably
For a simple download:
curl -L -O https://example.com/file.tar.gzFor a script, you may want failure detection and useful errors:
curl -fL -o file.tar.gz https://example.com/file.tar.gzThis combines:
-f
fail on HTTP errors
-L
follow redirects
-o
choose the output filenameThis is a useful general-purpose download pattern.
Continue A Partial Download
If a large download is interrupted, HTTP servers may support resuming it.
Use:
curl -C - -O https://example.com/large-file.isoThe -C - tells curl to continue from the existing local file position.
This is useful for large files where restarting the entire download would be wasteful.
Whether resume works depends on the remote server supporting the necessary range requests.
Download Multiple Files
You can invoke curl repeatedly:
curl -O https://example.com/file1.tar.gz
curl -O https://example.com/file2.tar.gz
curl -O https://example.com/file3.tar.gzYou can also use multiple URLs in one command:
curl -O https://example.com/file1.tar.gz \
-O https://example.com/file2.tar.gz \
-O https://example.com/file3.tar.gzThis is useful when you know exactly which resources you need.
Download A Whole Website: Why This Is Different
A common question is:
“Can
curldownload an entire website?”
Not in the way people usually mean.
A web page is not necessarily one file.
Suppose:
index.html
├── style.css
├── app.js
├── image.png
└── another-page.htmlDownloading:
curl -O https://example.com/gets one HTTP response.
It does not automatically crawl every linked page and dependency.
You could write a script that parses links and recursively requests them, but that quickly becomes a web crawler rather than a simple curl operation.
For a practical “download this site and its linked resources” task, a crawler-oriented tool such as wget is often a better fit.
That difference is important:
curl
→ excellent at making controlled HTTP requests
wget
→ designed more around downloading resources
and recursive retrievalWe’ll examine this distinction properly in the next lesson.
curl As A Service Debugging Tool
Imagine a service is supposed to expose:
https://api.example.com/healthA useful progression is:
curl -I https://api.example.com/healthIf you need connection details:
curl -v https://api.example.com/healthIf the endpoint expects JSON:
curl -H 'Accept: application/json' https://api.example.com/healthIf authentication is required:
curl -H 'Authorization: Bearer TEST_TOKEN' https://api.example.com/healthIf you need the HTTP status in a script:
curl -sS -o /dev/null -w '%{http_code}\n' https://api.example.com/healthThe important part is that each command answers a different question.
curl And The Layers You Have Learned
At this point, the networking topics connect together:
curl https://example.com
│
▼
DNS
│
▼
IP address
│
▼
Routing
│
▼
Network interface
│
▼
TCP connection to port 443
│
▼
TLS
│
▼
HTTP request
│
▼
HTTP responseThis is why curl is such a good networking tool for this course.
It doesn’t replace the lower-level commands.
Instead, it gives you a practical application whose behavior you can explain using those lower-level concepts.
When Should You Reach For curl?
A useful rule is:
Use
curlwhen you want to make or inspect a specific request.
Examples:
| Task | Useful curl pattern |
|---|---|
| Fetch a URL | curl URL |
| See response headers | curl -i URL |
| Request headers only | curl -I URL |
| Inspect the connection | curl -v URL |
| Follow redirects | curl -L URL |
| Download with chosen name | curl -o file URL |
| Download using remote name | curl -O URL |
| Send form data | curl -d '...' URL |
| Send JSON | curl -H 'Content-Type: application/json' -d '...' URL |
| Add a header | curl -H 'Name: value' URL |
| Show only status | curl -sS -o /dev/null -w '%{http_code}\n' URL |
| Fail on HTTP errors | curl -f URL |
| Set a timeout | curl --max-time 10 URL |
You don’t need to memorize this table. The goal is to recognize the patterns and know what question each one answers.
What You Should Remember
curl is much more than a downloader.
It is a controlled HTTP client that lets you see and manipulate the request/response exchange.
The most useful progression is:
curl URL
↓
basic response
curl -i URL
↓
response headers + body
curl -I URL
↓
headers only
curl -v URL
↓
connection + request + response details
curl -H ... -d ... URL
↓
reproduce a specific HTTP requestFor downloads:
-o
choose the local filename
-O
use the remote filename
-L
follow redirects
-C -
resume a partial downloadAnd for automation:
-f
treat HTTP errors as failures
-sS
quiet normal output but keep errors
--connect-timeout
limit connection establishment time
--max-time
limit the entire operationThe larger lesson is that curl gives you a way to reduce a web interaction to something explicit and reproducible:
request
↓
responseThat makes it useful both for ordinary downloads and for serious service debugging.
What’s Next
curl is excellent when you want precise control over an HTTP request. But downloading resources is only one part of the problem.
Next we’ll look at wget and use it to answer the practical question:
When should you use
wgetinstead ofcurl?
We’ll compare their strengths through real patterns, including downloads, recursive retrieval, and website mirroring.