curl sends HTTP requests from the terminal. You give it a URL, it makes the request, and prints the response. It supports every HTTP method (GET, POST, PUT, DELETE, PATCH), custom headers, authentication, file uploads, cookies, and TLS options. If a browser can make a request, curl can make the same request.
On a server, curl is the standard tool for testing endpoints, debugging API responses, checking if a site is responding, downloading files, and scripting interactions with web services. This guide covers the commands you will use most often, with practical examples.
What curl does#
curl (short for “Client URL”) transfers data using URLs. It supports HTTP, HTTPS, FTP, SFTP, and dozens of other protocols, but HTTP/HTTPS is what you will use it for 99% of the time.
When you run:
curl https://example.com
curl sends an HTTP GET request to
example.com
, receives the response, and prints the response body (the HTML) to your terminal. That is the simplest use case. Everything else builds on it by adding headers, changing the HTTP method, sending data, or controlling how the response is handled.
Installing curl#
curl comes pre-installed on most Linux distributions. Check if it is available:
curl --version
If it is not installed:
Ubuntu / Debian
sudo apt update && sudo apt install curl
CentOS / RHEL / AlmaLinux
sudo yum install curl
Or on newer versions:
sudo dnf install curl
Verify the installation
curl --version
This prints the curl version, supported protocols, and the TLS library it uses (usually OpenSSL). The version matters when troubleshooting TLS issues because older versions may not support TLS 1.3 or certain cipher suites.
GET requests#
Basic GET request
curl https://example.com
Sends a GET request and prints the response body. This is the default method — you do not need to specify it.
Get only the response headers
curl -I https://example.com
The
-I
flag sends a HEAD request, which returns only the headers without the body. Useful for checking status codes, content types, cache headers, and server information without downloading the full page.
HTTP/2 200
content-type: text/html; charset=UTF-8
server: nginx
cache-control: max-age=3600
Get headers and body together
curl -i https://example.com
The lowercase
-i
includes response headers at the top of the output, followed by the body. Useful when you need to see both.
Verbose output (full request and response)
curl -v https://example.com
The
-v
flag prints everything: the outgoing request headers, the TLS handshake details, the response headers, and the response body. This is the first flag to reach for when debugging. Lines prefixed with
>
are sent by curl. Lines prefixed with
<
are received from the server. Lines prefixed with
*
are curl’s own diagnostic messages.
Silent mode
curl -s https://example.com
The
-s
flag suppresses the progress meter and error messages. Useful in scripts where you only want the response body in the output, not curl’s status information.
Silent mode but show errors
curl -sS https://example.com
The
-S
flag re-enables error messages while keeping the progress meter hidden. This is usually what you want in scripts: clean output on success, error messages on failure.
POST requests#
Send a POST request with form data
curl -X POST -d "username=admin&password=secret" https://example.com/login
The
-X POST
sets the method to POST. The
-d
flag sends the data in the request body. curl automatically sets the
Content-Type
header to
application/x-www-form-urlencoded
.
You can omit
-X POST
when using
-d
because curl automatically switches to POST when you provide data:
curl -d "username=admin&password=secret" https://example.com/login
Send JSON data
curl -X POST \
-H "Content-Type: application/json" \
-d '{"username": "admin", "email": "admin@example.com"}' \
https://api.example.com/users
The
-H
flag sets a custom header. When sending JSON, you must set the
Content-Type
header to
application/json
because curl does not do this automatically.
Send JSON from a file
curl -X POST \
-H "Content-Type: application/json" \
-d @data.json \
https://api.example.com/users
The
@
prefix tells curl to read the data from a file instead of the command line argument.
Send form data with file upload
curl -F "file=@photo.jpg" -F "description=Profile photo" https://example.com/upload
The
-F
flag sends data as
multipart/form-data
, which is the encoding used for file uploads in HTML forms. The
@
prefix before the filename tells curl to read and upload that file.
PUT requests#
Send a PUT request with JSON
curl -X PUT \
-H "Content-Type: application/json" \
-d '{"email": "new@example.com"}' \
https://api.example.com/users/42
PUT is typically used to update an existing resource. The syntax is the same as POST, just with
-X PUT
.
Send a PUT request with form data
curl -X PUT -d "status=active" https://api.example.com/users/42
PUT with a file
curl -X PUT -T file.txt https://example.com/upload/file.txt
The
-T
flag uploads a file using PUT. This is different from
-F
(which uses multipart form encoding).
-T
sends the raw file contents as the request body.
DELETE requests#
curl -X DELETE https://api.example.com/users/42
Sends a DELETE request. Most APIs require authentication for delete operations — see the authentication section below.
PATCH requests#
curl -X PATCH \
-H "Content-Type: application/json" \
-d '{"status": "inactive"}' \
https://api.example.com/users/42
PATCH is used for partial updates. Unlike PUT (which typically replaces the entire resource), PATCH updates only the fields you include.
Query parameters#
Query parameters are part of the URL, appended after a
?
:
curl "https://api.example.com/search?q=nginx&limit=10"
Always quote the URL when it contains
&
because the shell interprets
&
as “run in background.” Without quotes:
# WRONG - shell runs "curl https://api.example.com/search?q=nginx" in the background
# and tries to run "limit=10" as a separate command
curl https://api.example.com/search?q=nginx&limit=10
For complex query strings, use
--data-urlencode
with
-G
to let curl handle the encoding:
curl -G \
--data-urlencode "q=nginx error log" \
--data-urlencode "limit=10" \
https://api.example.com/search
The
-G
flag forces GET (instead of the POST that
-d
and
--data-urlencode
normally trigger). curl URL-encodes the values and appends them to the URL as query parameters. The spaces in “nginx error log” become
%20
automatically.
Custom headers#
Set a single header
curl -H "Authorization: Bearer eyJhbGciOi..." https://api.example.com/data
Set multiple headers
curl \
-H "Authorization: Bearer eyJhbGciOi..." \
-H "Accept: application/json" \
-H "X-Custom-Header: value" \
https://api.example.com/data
Repeat the
-H
flag for each header.
Override the User-Agent
curl -A "Mozilla/5.0" https://example.com
The
-A
flag sets the
User-Agent
header. By default, curl sends
curl/version
as the user agent. Some servers or CDNs block requests from curl. Setting a browser-like user agent can work around this, though it is generally better to figure out why the server is blocking curl.
Following redirects#
By default, curl does not follow HTTP redirects (301, 302, etc.). It prints the redirect response and stops:
curl http://example.com
If
http://example.com
redirects to
https://example.com
, curl prints the 301 response and the redirect location, but does not follow it.
Follow redirects
curl -L http://example.com
The
-L
flag tells curl to follow redirects. curl follows up to 50 redirects by default. If a URL redirects more than that, it stops to prevent infinite loops.
Limit the number of redirects
curl -L --max-redirs 5 http://example.com
SSL/TLS options#
Ignore SSL certificate errors (-k)
curl -k https://example.com
The
-k
(or
--insecure
) flag tells curl to skip certificate verification. curl connects even if the certificate is expired, self-signed, or has a hostname mismatch.
When to use this:
- Testing against a development server with a self-signed certificate
- Debugging a production SSL issue to see if the problem is the certificate or something else
- Accessing an internal tool that uses a self-signed certificate
When NOT to use this:
- In production scripts that connect to external services. Disabling verification means you cannot trust that you are connecting to the right server. A man-in-the-middle could intercept the connection.
- As a permanent fix for certificate errors. Fix the certificate instead.
For understanding how SSL certificates work and how to install them properly, see How to install an SSL certificate.
Specify a TLS version
curl --tlsv1.2 https://example.com
Forces curl to use TLS 1.2 or later. Useful for testing whether a server supports a specific TLS version.
curl --tlsv1.3 https://example.com
Forces TLS 1.3. If the server does not support TLS 1.3, the connection fails.
Check a site’s SSL certificate
curl -vI https://example.com 2>&1 | grep -A 6 "Server certificate"
The verbose output (
-v
) includes the TLS handshake and certificate details. This pipes the output through grep to extract just the certificate information.
Saving output to a file#
Save the response body
curl -o page.html https://example.com
The
-o
flag writes the output to the specified file instead of the terminal.
Save with the remote filename
curl -O https://example.com/files/backup.tar.gz
The
-O
flag saves the file using the filename from the URL (
backup.tar.gz
in this case).
Download multiple files
curl -O https://example.com/file1.tar.gz -O https://example.com/file2.tar.gz
Resume a broken download
curl -C - -O https://example.com/large-file.tar.gz
The
-C -
flag tells curl to resume the download from where it left off. curl checks the local file size and requests the remaining bytes. Useful for large files on unreliable connections.
Authentication#
Basic authentication
curl -u username:password https://api.example.com/data
The
-u
flag sends HTTP Basic authentication credentials. curl base64-encodes the credentials and sends them in the
Authorization
header.
If you omit the password, curl prompts for it:
curl -u username https://api.example.com/data
Bearer token authentication
curl -H "Authorization: Bearer your-token-here" https://api.example.com/data
Most modern APIs use bearer tokens rather than basic auth. Set the token as a header.
Timeouts#
Connection timeout
curl --connect-timeout 5 https://example.com
Gives up if curl cannot establish a connection within 5 seconds. Without this, curl waits for the system’s default TCP timeout, which can be minutes.
Total timeout
curl --max-time 30 https://example.com
Gives up if the entire operation (connection + transfer) takes more than 30 seconds. Useful in scripts to prevent curl from hanging indefinitely on slow or unresponsive servers.
Both together (recommended for scripts)
curl --connect-timeout 5 --max-time 30 https://example.com
Scripting with curl in bash#
Check if a site is responding
#!/bin/bash
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://example.com)
if [ "$STATUS" -eq 200 ]; then
echo "Site is up"
else
echo "Site returned status $STATUS"
fi
The flags:
-
-s— silent mode (no progress meter) -
-o /dev/null— discard the response body -
-w "%{http_code}"— print only the HTTP status code
Measure response time
curl -s -o /dev/null -w "DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTLS: %{time_appconnect}s\nFirst byte: %{time_starttransfer}s\nTotal: %{time_total}s\n" https://example.com
The
-w
flag supports format variables for timing. This breaks down the request into DNS resolution, TCP connection, TLS handshake, time to first byte, and total time. Useful for diagnosing slow responses.
Loop through URLs
#!/bin/bash
URLS=(
"https://example.com"
"https://example.com/about"
"https://example.com/contact"
)
for URL in "${URLS[@]}"; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL")
echo "$STATUS $URL"
done
Send an alert on failure
#!/bin/bash
STATUS=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 https://example.com)
if [ "$STATUS" -ne 200 ]; then
echo "ALERT: example.com returned $STATUS at $(date)" >> /var/log/site-monitor.log
fi
This can be run via cron to monitor a site at regular intervals.
Use curl in a conditional
if curl -s --max-time 5 https://example.com > /dev/null 2>&1; then
echo "Site is reachable"
else
echo "Site is down or unreachable"
fi
curl returns exit code 0 on success and non-zero on failure, so it works directly in
if
statements.
Common curl errors and fixes#
curl: (6) Could not resolve host
DNS resolution failed. The domain name could not be translated to an IP address.
Check: is the domain spelled correctly? Can you resolve it with
dig
or
nslookup
?
dig example.com
If DNS works but curl fails, check if
/etc/resolv.conf
has valid nameservers.
curl: (7) Failed to connect to host
curl resolved the hostname but could not establish a TCP connection.
Common causes:
- The server is down
- A firewall is blocking the port
- The wrong port is being used
Check: can you reach the port with telnet or nc?
nc -zv example.com 443
curl: (28) Connection timed out
curl waited for a response but none came within the timeout period.
Common causes:
- Network issues between you and the server
- Server is overloaded and not responding
- Firewall silently dropping packets (no rejection, just silence)
Fix: increase the timeout with
--connect-timeout
or
--max-time
if the server is just slow. If it consistently times out, the problem is network-level.
curl: (35) SSL connect error
The TLS handshake failed.
Common causes:
- Server only supports old TLS versions that curl has disabled
- Certificate is misconfigured
- Intermediate certificates are missing from the chain
Debug: use verbose mode to see the handshake details:
curl -v https://example.com
Look at the
*
lines for TLS-related errors.
curl: (51) SSL: certificate subject name does not match target host name
The certificate is valid but it was issued for a different domain than the one you are connecting to.
Example: you connect to
api.example.com
but the certificate is for
www.example.com
.
Fix: ensure the certificate covers the correct domain. If using a wildcard certificate (
*.example.com
), note that it does not cover the bare domain (
example.com
).
curl: (56) Recv failure: Connection reset by peer
The server accepted the connection but then abruptly closed it.
Common causes:
- Server-side crash or misconfiguration
- WAF or security software blocking the request
- Rate limiting
Try: adding a browser-like
User-Agent
header, or check if the server has rate limiting that is rejecting your requests.
curl: (60) SSL certificate problem: unable to get local issuer certificate
curl cannot verify the server’s certificate because it does not have the CA certificate in its trust store.
Common causes:
- The server is not sending the intermediate certificate chain
- curl’s CA bundle is outdated
- You are behind a corporate proxy that uses its own CA
Quick test: use
-k
to bypass verification and confirm the rest of the request works. Then fix the certificate chain on the server or update the CA bundle.
Common flag reference#
| Flag | Meaning |
|---|---|
-X METHOD
| Set the HTTP method (GET, POST, PUT, DELETE, PATCH) |
-d "data"
| Send data in the request body (implies POST) |
-H "Header: value"
| Set a custom header |
-i
| Include response headers in output |
-I
| Show only response headers (HEAD request) |
-v
| Verbose output (full request/response details) |
-s
| Silent mode (suppress progress meter) |
-S
| Show errors even in silent mode |
-o file
| Write output to file |
-O
| Save with remote filename |
-L
| Follow redirects |
-k
| Skip SSL certificate verification |
-u user:pass
| HTTP Basic authentication |
-A "agent"
| Set User-Agent header |
-F "field=value"
| Send multipart form data |
-T file
| Upload file with PUT |
-C -
| Resume interrupted download |
--connect-timeout N
| Connection timeout in seconds |
--max-time N
| Total operation timeout in seconds |
-w "format"
| Print information after transfer |
curl on Hostney#
On Hostney, curl is available via SSH in your account container. You can use it to test endpoints, debug connectivity, check if external APIs are reachable from your server, and download files.
Connect via SSH using the credentials from the Terminal Access section in the control panel. For running curl commands remotely without an interactive session, see How to run commands over SSH.