Skip to main content
Blog|
How-to guides

curl Command: How to Make HTTP Requests from the Command Line

|
Mar 19, 2026|11 min read
HOW-TO GUIDEScurl Command: How to Make HTTPRequests from the Command LineHOSTNEYhostney.comMarch 19, 2026

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#

FlagMeaning
-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.