A guide to cURL
Table of Contents
- A guide to cURL
- What is cURL?
- Chapter 1: Anatomy of cURL command
- The Basics
- Following Paths
- Chapter 2: Speak HTTP
- Verbs/Methods
- Modifying the Header
- Sending Data
- Chapter 3: The Inspector's Toolkit
- Verbose Mode (-v)
- Even Deeper: Trace
- Silent Mode (-s)
- Timing is Everything
- Chapter 4: Handling Edge Cases
- Authentication
- Cookies
- Dealing with SSL
- Chapter 5: cURL in the Wild
- Health Checks
- Testing Your Own API
- Comparing HTTP Versions
- Downloading Files
- Chapter 6: cURL vs The World
- cURL vs Postman
- cURL vs Wget
- Conclusion
- Cheat Sheet
- What's Next?
A guide to cURL
Let's take this very slow. Take a deep breath.
Objective:
- Second principle thinking for URL testing
- Fun
- More fun
Even though people don't understand, most of us already ran this command in their terminal when the endpoint is not reachable in browser:
curl google.com
If it was up, we would get something like:
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
Another advanced command that helps us retrieve the status code instead of the http header.
curl -o /dev/null -s -w "%{http_code}" https://phind.com
phind.comwas a AI search platform. Unfortunetely they couldn't continue. Therefore, striking the endpoint will return a403code.
What is cURL?
cURL stands for {client URL}.
curl serves as a command-line and scripting tool for data transfers, while its core library, libcurl, powers connectivity in diverse devices—from cars and TVs to routers, printers, audio gear, phones, tablets, medical equipment, set-top boxes, games, and media players—driving the internet transfer backbone for billions of software installations worldwide. curl
Practically every internet user on the planet relies on curl each day.
Who is this for?
- Devs: To test endpoints, debug integrations, and understand HTTP.
- Testers: For quick API health checks, load simulation, and automation.
- Architects: To inspect traffic, verify security headers, and design robust systems.
- Hobbyists
Chapter 1: Anatomy of cURL command
curl [options] [url]
You can identify various commands with: curl --help
The Basics
curl https://reflection.amals.xyz`
We discussed this before, it is a simple GET request.
curl -O https://reflection.amals.xyz/posts/media/CS/virtual-file-system.svg
It will download and save with the original filename. This can be any file.
curl -o myfile.html https://reflection.amals.xyz
-o can be used to save the response to a specific filename. Here, the html file is saved with content of my root html file.
Following Paths
curl -L https://google.com
We already seen the 3XX http headers. Without passing the --location|-L the curl will respond with the header file of the redirect. As we seen in the first example in the blog. By default cURL can go upto 50 redirects and get the information.
Activity: Run the commands with and without -L and grep the keyword like search.
Chapter 2: Speak HTTP
This section is almost helpful for developers and testers. Also for people who wanted to verify their API documentation.
Verbs/Methods
Until this point we have only used GET request. Let's do POST:
Calling syntax:
curl -X [method] [url]
curl -X POST https://api.example.com/users
Verbs: POST, PUT, DELETE
So you might have asked, what data are we passing?
We can add payload to the request.
Practical example:
curl -X POST -H "Content-Type: application/json" \
-d '{"name":"Amal"}' https://api.example.com/users
-dis used to load the data.-His used for specifying the content type.
I don't have my endpoint to test the API for the given example. So the credit is for learning.
Modifying the Header
Adding Headers
We can use the same -H or --header to send custom HTTP headers like auth tokens:
curl -H "Authorization: Bearer YOUR_TOKEN" https://api.example.com/protected
Example:
curl -X POST https://api.groq.com/openai/v1/chat/completions \
-H "Authorization: Bearer GROQ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [{"role": "user", "content": "Hello!"}],
"model": "llama-3.1-8b-instant"
}'
User-Agent Spoofing
-A or --user-agent overrides the default curl identifier:
curl -A "Mozilla/5.0 (compatible; MyBot/1.0)" https://example.com
Setting Referer
The -e or --referer option sets the HTTP "Referer" header, which tells the target server what page "referred" the request to that server. Like faking where the request came from.
curl -e "https://my-site.com" https://target.com
Why? Because websites often check the referer for security, analytics, or anti-bot protection. This makes target.com think we are clicked from my-site.com.
Real example:
curl -e "https://www.google.com/" https://example.com
# Check headers received
curl -I -e "https://facebook.com" https://x.com
Sending Data
We saw -d for JSON data. But we will discuss more:
Form Data (URL-encoded):
curl -d "username=amal&password=secret123" https://example.com/login
Binary Data:
When you need to send raw files or binary data, use --data-binary:
curl --data-binary @image.png -H "Content-Type: image/png" https://api.example.com/upload
The @ tells curl to read from a file. Without it, the literal text gets sent.
Multipart Forms (File Uploads):
This is how you upload files with forms:
curl -F "avatar=@/home/amal/photo.jpg" -F "bio=Hello" https://api.example.com/profile
The -F flag simulates a multipart form submission - perfect for profile pictures, document uploads, or anything that requires both files and text fields.
Chapter 3: The Inspector's Toolkit
We are taking an advance level here.
Verbose Mode (-v)
This is the most important flag. Ever. It shows everything - request headers, response headers, TLS handshake, the whole conversation:
curl -v https://example.com
You'll see something like:
* Trying 93.184.216.34:443...
* Connected to example.com (93.184.216.34) port 443 (#0)
> GET / HTTP/1.1
> Host: example.com
> User-Agent: curl/8.0.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html
< Content-Length: 1256
<
<!doctype html>
...
The > lines are what curl sent. The < lines are what came back. This is gold when your API isn't working properly.
Even Deeper: Trace
When -v isn't enough, go full detective with --trace:
curl --trace trace.txt https://example.com
This dumps every single byte to trace.txt. It's overwhelming, but sometimes you need to see exactly what's happening on the wire.
Or for ASCII-friendly output:
curl --trace-ascii trace.txt https://example.com
Silent Mode (-s)
Sometimes you don't want all that noise. Just the data:
curl -s https://api.example.com/data
The progress meter disappears. Useful for scripting.
Want to suppress errors but still see output? Combine with -S:
curl -sS https://example.com
Timing is Everything
This is where architects drool. The -w flag outputs timing information after the transfer:
curl -w "Time: %{time_total}s\n" -o /dev/null -s https://example.com
But here's the real magic - all the timing variables:
| Variable | What it measures |
|---|---|
time_namelookup |
DNS resolution time |
time_connect |
TCP handshake time |
time_appconnect |
TLS/SSL handshake time |
time_starttransfer |
Time to first byte (TTFB) |
time_total |
Total request time |
Real example - measuring API latency:
curl -w "DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTLS: %{time_appconnect}s\nFirst Byte: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
-o /dev/null -s https://api.groq.com/v1/models
This tells you exactly where your latency is coming from. DNS slow? That's your network. TLS slow? Certificate issues? First byte slow? The server is thinking too hard.
Chapter 4: Handling Edge Cases
Real-world scenarios need more than basic requests. Also means, this will not be used most of the time.
Authentication
Basic Auth:
curl -u username:password https://example.com/secret
The -u flag handles the Base64 encoding for you. Clean and simple.
Bearer Tokens:
We already saw this, but here's a reminder:
curl -H "Authorization: Bearer YOUR_TOKEN" https://api.example.com/protected
Cookies
Cookies are just headers, but curl makes them easy.
Sending cookies:
curl -b "session_id=abc123" https://example.com/dashboard
Saving cookies:
curl -c cookies.txt https://example.com/login -d "user=amal"
Using saved cookies:
curl -b cookies.txt https://example.com/dashboard
This is how you test login flows - save the session cookie, then use it for subsequent requests.
Dealing with SSL
Sometimes you need to ignore certificate errors. Development, testing with self-signed certs:
curl -k https://self-signed.example.com
For more info, by defualt curl will use TLS/SSL certificate. Using -k skips that step.
Warning: Never use
-kin production. Only for local development or testing.
Want to see certificate details?
curl -v https://example.com 2>&1 | grep -i certificate
Or get full certificate info:
curl --cert-status -v https://example.com
Chapter 5: cURL in the Wild
Let's see how this actually gets used.
Health Checks
The classic sysadmin one-liner:
curl -sf https://myapp.com/health && echo "App is up" || echo "App is down"
The flags: -s (silent), -f (fail on HTTP errors). If the health endpoint returns 200, it echoes "App is down" and exits with error.
Add this to cron for monitoring.
Testing Your Own API
You just wrote a new endpoint. Test it:
curl -X POST http://localhost:8080/api/v1/users \
-H "Content-Type: application/json" \
-d '{"name": "Amal", "email": "amal@example.com"}'
Or load the body from a file:
curl -X POST http://localhost:8080/api/v1/users \
-H "Content-Type: application/json" \
-d @user.json
That @file syntax is so useful.
Comparing HTTP Versions
Architects care about performance. Let's compare HTTP/1.1 vs HTTP/2:
# HTTP/2
curl --http2 -w "HTTP/2: %{time_total}s\n" -o /dev/null -s https://example.com
# HTTP/1.1
curl --http1.1 -w "HTTP/1.1: %{time_total}s\n" -o /dev/null -s https://example.com
Run these a few times and you'll see the difference. (HTTP/2 usually wins for multiple requests due to multiplexing).
Downloading Files
Download with progress bar:
curl -O https://example.com/big-file.zip
Resume a failed download:
curl -C - -O https://example.com/big-file.zip
The -C - tells curl to continue from where it left off.
Chapter 6: cURL vs The World
cURL vs Postman
Postman is great for building and organizing requests. It's a GUI. You can actually import curl commands into Postman:
Import -> Paste Raw Text -> Paste your curl command
cURL is the raw, scriptable form. It's universal. Every language can execute a shell command. Paste a curl into Python, Node, Go, it just works.
==Use Postman for complex workflows. Use curl for quick tests and automation.==
cURL vs Wget
| Feature | cURL | Wget |
|---|---|---|
| Protocols | HTTP, HTTPS, FTP, SFTP, and many more | FTP, HTTP, HTTPS |
| Recursive downloads | No (single files) | Yes (mirroring) |
| POST data | Yes | Yes, but limited |
| Library (libcurl) | Yes | No |
Wget is for wget -r https://example.com - recursively downloading entire websites.
cURL is for precise, single-endpoint interactions.
They complement each other.
Conclusion
The terminal doesn't lie. What you see is what you get.
Cheat Sheet
| Category | Flag | Description | Example |
|---|---|---|---|
| Core | -X |
HTTP Method | -X POST |
| Data | -d |
Send data | -d "key=val" |
| Data | -F |
Form upload | -F "file=@img.jpg" |
| Headers | -H |
Custom Header | -H "Content-Type: application/json" |
| Auth | -u |
Basic Auth | -u user:pass |
| Debug | -v |
Verbose output | -v https://site.com |
| Debug | -w |
Write-out timing | -w "%{time_total}" |
| Output | -o |
Save to file | -o index.html |
| Output | -O |
Save with original name | -O |
| Follow | -L |
Follow redirects | -L |
| Cookies | -b |
Send cookies | -b "session=x" |
| Cookies | -c |
Save cookies | -c cookies.txt |
| Insecure | -k |
Skip SSL check | -k |
| Silent | -s |
Silent mode | -s |
What's Next?
Pick one flag from this list you haven't used. Open your terminal. Try it.
That's how you learn.