cURL Command in Linux (With Examples)

Last updated Friday, February 6, 2026 10:32 am Joshua James 17 min read

cURL is a command-line tool for transferring data to and from servers using URLs. It supports HTTP, HTTPS, FTP, SMTP, and dozens of other protocols, making it the go-to tool for downloading files, testing REST APIs, debugging network issues, and scripting automated transfers. This guide covers cURL syntax fundamentals, practical examples for downloads, uploads, and API workflows, authentication and TLS options, network tuning, non-HTTP protocols, and troubleshooting common errors.

If you need a comparison for bulk file downloads, pair this guide with our wget command examples to understand when each tool fits best.

Understand the cURL Command Syntax

Every cURL invocation follows the same basic pattern. Understanding this structure lets you build commands confidently, regardless of the protocol or task.

curl [options] [URL]
  • curl: The binary itself. Most Linux distributions include it in PATH by default, so you can call it from scripts or interactive terminals without specifying the full path.
  • [options]: Flags that modify behavior. You can chain as many as you need to control authentication, redirects, headers, output files, timeouts, and protocol details.
  • [URL]: The target endpoint. cURL accepts multiple URLs in a single invocation, and you can mix schemes like https://, ftp://, and smtp:// in one command.

At its simplest, cURL fetches a resource and prints it to standard output:

curl https://example.com

From there, layer on flags to shape the request. The most commonly used options include:

  • -o file: Write response data to a file you specify.
  • -O: Save the response using the remote filename from the URL.
  • -L: Follow HTTP redirects automatically (important for CDN-hosted downloads and short URLs).
  • -H: Add a custom request header.
  • -d or --data: Send request body data (triggers a POST request by default).
  • -u user:pass: Supply HTTP Basic authentication credentials.
  • -I: Fetch response headers only, without the body.
  • -v: Enable verbose output for debugging the full request/response handshake.
  • --json: Send JSON data with automatic Content-Type and Accept headers (available in cURL 7.82 and later).

For quick reference, this table groups the most useful flags by task:

TaskUseful OptionsWhat They Do
Downloads-O, -o, -C -, --limit-rateControl filenames, resume interrupted downloads, and throttle bandwidth.
APIs and Data-d, --json, --data-urlencode, -HPost JSON or form data and set custom headers for API calls.
Authentication-u, --oauth2-bearer, --certHandle Basic auth, bearer tokens, or mutual TLS client certificates.
Debugging-v, --trace, -w, --fail-with-bodyInspect the full handshake, measure timing, and fail fast on errors.
Networking-x, --resolve, --connect-timeoutRoute through proxies, override DNS, and set timeouts.

Install the cURL Command

Check Your Existing cURL Installation

Most Linux distributions include cURL by default. Verify the installed version to confirm which features and protocols are available:

curl --version

The output shows the cURL version, supported protocols, and enabled features. The exact version and protocol list vary by distribution:

curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3
Release-Date: 2023-12-06
Protocols: dict file ftp ftps gopher gophers http https imap imaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS HSTS HTTP2 HTTPS-proxy IPv6 Largefile libz NTLM SSL threadsafe TLS-SRP UnixSockets

If curl is missing (common on minimal server images and containers), install it with the package manager for your distribution.

Ubuntu and Debian

sudo apt update
sudo apt install curl

Fedora

sudo dnf install curl

CentOS Stream, AlmaLinux, and Rocky Linux

RHEL 9-based distributions (CentOS Stream 9, AlmaLinux 9, Rocky Linux 9) ship curl-minimal instead of full curl by default. The minimal build only supports a handful of protocols (file, ftp, ftps, http, https) and is missing support for SCP, SFTP, SMTP, LDAP, DICT, and others. Running sudo dnf install curl fails because curl-minimal conflicts with the full package.

Replace curl-minimal with the full curl package using the swap command:

sudo dnf swap curl-minimal curl

This removes curl-minimal and installs full curl with all protocol support in a single transaction.

CentOS Stream 10 and later ship the full curl package by default, so this swap step is only necessary on EL9-based systems.

Arch Linux

sudo pacman -S curl

Alpine Linux

sudo apk add curl

openSUSE

sudo zypper install curl

Gentoo

sudo emerge net-misc/curl

Void Linux

sudo xbps-install curl

Download and Upload Files with cURL

cURL handles everything from quick one-off downloads to scripted multi-file transfers. The examples below cover the workflows you will use most often.

Fetch Output to the Terminal

The simplest cURL call sends a GET request and prints the response body to standard output:

curl https://example.com

Pipe the output through jq (a lightweight JSON processor) or filter it with the grep command to extract specific fields directly in a pipeline.

Save a File with Remote or Custom Names

Use -O (uppercase letter O) to save a file using the server-provided filename, or -o (lowercase) to choose your own output name:

# Keep the original filename from the URL
curl -O https://downloads.example.com/app.tar.gz

# Choose a custom output filename
curl -o latest-release.tar.gz https://downloads.example.com/app.tar.gz

Add -L when the download link redirects through a short URL or CDN. Combine with --fail-with-body to stop on HTTP errors and avoid saving error pages as if they were real files:

curl -L --fail-with-body -O https://downloads.example.com/app.tar.gz

To prevent infinite redirect loops from malicious or misconfigured URLs, limit the number of redirects cURL will follow:

curl -L --max-redirs 5 -O https://short.link/abc123

Resume Interrupted Downloads

If a network drop or timeout interrupts your download, resume it with -C -. cURL checks the size of the existing partial file and requests only the remaining bytes from the server:

curl -C - -O https://downloads.example.com/app.tar.gz

Resume requires the server to support HTTP range requests. Check for an Accept-Ranges: bytes header in the response (curl -I URL) before relying on this feature for critical transfers.

Download Multiple Files in One Command

Queue several URLs in a single cURL invocation to download multiple files without looping in a script:

curl -O https://downloads.example.com/file1.tar.gz \
     -O https://downloads.example.com/file2.tar.gz

cURL processes the URLs from left to right. If you need the output saved into nested directories that do not exist yet, use -o /path/to/file.tar.gz with --create-dirs for each URL.

Show Download Progress

Display a simple progress bar for large downloads using -# (hash sign):

curl -# -O https://downloads.example.com/large-file.iso
######################################################################## 100.0%

Without -#, cURL shows detailed transfer statistics including speed, time elapsed, and bytes transferred. Use -s (silent) to suppress all progress output in scripts.

Run Downloads in the Background

Send long transfers to the background so you can continue using the shell:

curl -O https://downloads.example.com/image.qcow2 &

Check progress with jobs -l or bring the task back to the foreground with fg. For long-running downloads on remote servers, run cURL inside a terminal multiplexer like tmux or screen so the session survives disconnections.

Upload Files to an HTTP Endpoint

Send a file to a server that accepts PUT uploads or multipart form-based submissions:

# HTTP PUT upload
curl -T backup.tar.gz https://uploads.example.com/backups/backup.tar.gz

# Multipart form upload (mirrors browser form submission)
curl -F "file=@backup.tar.gz" https://uploads.example.com/api/import

The -F flag builds a multipart form request with boundary markers so the server receives proper metadata alongside the file, just like a browser form submission.

Work with Web APIs Using cURL

cURL is the fastest way to test API endpoints, experiment with headers, or script service-to-service calls from the terminal.

Send Custom Headers

Add one or more headers with -H to include authorization tokens, set content types, or simulate browser requests:

curl -H "Accept: application/json" \
     -H "User-Agent: linuxcapable-cli" \
     https://api.example.com/v1/health

This pattern keeps test calls aligned with production clients, especially when services inspect user agents or content negotiation headers.

Send JSON with the –json Flag

cURL 7.82 and later include the --json shortcut, which automatically sets Content-Type: application/json, Accept: application/json, and sends a POST request in one flag. This replaces the older three-flag pattern:

# Modern approach (cURL 7.82+)
curl --json '{"title":"Network latency","priority":"high"}' \
     https://api.example.com/v1/tickets

# Equivalent older approach
curl -X POST https://api.example.com/v1/tickets \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -d '{"title":"Network latency","priority":"high"}'

A successful POST typically returns the created resource:

{
  "id": 1234,
  "title": "Network latency",
  "priority": "high",
  "status": "open",
  "created_at": "2025-11-15T10:30:45Z"
}

Use single quotes around the JSON payload so the shell does not interpret special characters. If the body includes escaped newlines or must be sent byte-for-byte, switch to --data-binary instead of -d.

Submit Form Data

Encode form fields the same way browsers do by using --data-urlencode for individual entries:

curl https://api.example.com/v1/search \
     --data-urlencode "query=error logs" \
     --data-urlencode "limit=25"

This method handles spaces, ampersands, and special characters correctly without requiring you to manually percent-encode the values.

Send Data from a File

Point cURL at a file when you keep request bodies under version control or generate them from templates:

curl -X POST https://api.example.com/v1/import \
     -H "Content-Type: application/json" \
     -d @payload.json

The @ prefix tells cURL to read the file contents verbatim, which keeps large JSON blobs and newline-heavy payloads intact.

Send DELETE or PUT Requests

Override the default GET method when interacting with RESTful endpoints that update or remove resources:

# Delete a record by ID
curl -X DELETE https://api.example.com/v1/tickets/42 \
     -H "Authorization: Bearer $API_TOKEN"

# Replace a record with PUT
curl -X PUT https://api.example.com/v1/tickets/42 \
     -H "Content-Type: application/json" \
     -d '{"status":"closed"}'

Pair these verbs with --fail-with-body when automating API calls so scripts stop on HTTP errors while still capturing the error response body for debugging.

Manage Cookies and Sessions

Read cookies from a file or send lightweight session data inline:

# Send a cookie inline
curl -b "session=abc123" https://www.example.com

# Save cookies from a login request to a file
curl -c cookies.txt -d "user=admin&pass=secret" https://api.example.com/login

# Reuse saved cookies in a follow-up request
curl -b cookies.txt https://api.example.com/dashboard

The -c flag writes server-set cookies to the file, and -b reads them back. This lets you maintain authenticated sessions across multiple cURL commands without re-authenticating each time.

Override User Agent Strings

Some services gate behavior based on the user agent string, which is the identifier your client sends to the server. Override it with -A:

curl -A "Mozilla/5.0" https://www.example.com

This is useful when testing how a server responds to different clients or when API documentation requires a specific user agent.

Handle Authentication and Security with cURL

APIs and secured endpoints frequently require credentials or stricter TLS settings. cURL provides concise flags for each scenario.

Authenticate with Basic Credentials

Supply a username with -u. cURL prompts for the password interactively if you omit it from the command, which avoids storing secrets in shell history:

curl -u admin https://api.example.com/v1/metrics

For automated scripts, read credentials from an environment variable or a secrets manager instead of hardcoding them on the command line.

Send Bearer or API Tokens

Most modern APIs expect tokens in the Authorization header:

curl https://api.example.com/v1/profile \
     -H "Authorization: Bearer $API_TOKEN"

Export the token in your shell session or read it from a secrets manager so the same script runs across environments without code changes.

Use Client Certificates for Mutual TLS

Mutual TLS (mTLS) endpoints require both a client certificate and a private key:

curl https://secure.example.com/report \
     --cert /etc/ssl/certs/client.pem \
     --key /etc/ssl/private/client.key

Verify that key file permissions are restrictive (chmod 600) before using them. Check our chmod command guide if you need to adjust permissions. Some providers expect the certificate and key combined in a single PEM file; consult your provider’s documentation.

Validate TLS Configuration

cURL validates HTTPS certificates by default. Use -k (or --insecure) only for short-lived testing against self-signed certificates in lab environments. Always switch back to proper validation after testing.

Point cURL at a custom certificate authority bundle to validate certificates from internal or private CAs:

curl --cacert /etc/ssl/certs/internal-ca.crt https://staging.internal.example.local/health

If you must temporarily bypass verification on a staging host:

curl -k https://staging.internal.example.local/health

Switch back to --cacert or a trusted certificate as soon as the test is done. Never use -k in production scripts.

Debug and Inspect HTTP Responses with cURL

cURL includes detailed diagnostic options that save time when you need to trace headers, measure latency, or investigate unexpected behavior.

Inspect Response Headers

Fetch response headers without the body to quickly check status codes, cache behavior, or redirect targets:

curl -I https://example.com/docs
HTTP/2 200
date: Fri, 15 Nov 2025 10:30:45 GMT
content-type: text/html; charset=UTF-8
server: nginx/1.24.0
cache-control: max-age=3600
etag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
x-frame-options: DENY
content-length: 12450

Add -L to follow redirects until you reach the final destination, then inspect the final response headers.

Save Headers and Body to Separate Files

Store headers in one file and the response payload in another for scripted comparisons or automated testing:

curl -D response.headers \
     -o response.json \
     https://api.example.com/v1/summary

Enable Verbose Mode and Trace Timing

Turn on verbose mode with -v to see the complete request and response handshake, including TLS negotiation, HTTP headers sent, and headers received:

curl -v https://api.example.com/v1/health
* Trying 192.0.2.15:443...
* Connected to api.example.com (192.0.2.15) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
> GET /v1/health HTTP/2
> Host: api.example.com
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/2 200
< content-type: application/json
<
{"status":"healthy","uptime":3600}

For performance analysis, use the -w (write-out) flag with format variables to extract specific timing metrics:

curl -o /dev/null -sS -w "DNS: %{time_namelookup}s | Connect: %{time_connect}s | TLS: %{time_appconnect}s | Total: %{time_total}s\n" \
     https://api.example.com/v1/health
DNS: 0.028s | Connect: 0.045s | TLS: 0.112s | Total: 0.342s

This breakdown reveals exactly where latency occurs: DNS resolution, TCP connection, TLS handshake, or server processing. For even deeper analysis, add --trace-time to prefix each trace line with a high-resolution timestamp.

Check HTTP Status Codes in Scripts

Extract just the HTTP status code for use in monitoring scripts or link validation tools:

curl -o /dev/null -s -w "%{http_code}\n" https://example.com/page
200

Use this pattern in health checks, CI/CD pipelines, or batch link validators to detect 404s, 500s, or redirect chains without downloading full response bodies.

Control Network Behavior with cURL

Beyond default transfers, cURL provides flags for proxy routing, custom DNS resolution, bandwidth throttling, timeouts, compression, and protocol negotiation.

Route Traffic Through a Proxy

Point cURL at an HTTP or SOCKS proxy, with optional credentials:

curl -x http://proxy.example.com:8080 \
     -U proxyuser:proxypass \
     https://api.example.com/v1/health

Prefix the proxy URL with socks5h:// to route DNS resolution through the SOCKS proxy as well, which prevents DNS leaks. Store proxy credentials in environment variables or a .netrc file to keep them out of shell history and process listings.

Override DNS Resolution

Test against staging hosts or force traffic to specific backends without modifying /etc/hosts:

curl --resolve api.example.com:443:192.0.2.15 \
     https://api.example.com/v1/health

cURL connects to 192.0.2.15 but still sends api.example.com in the Host header and TLS SNI, which makes this ideal for blue/green deployments, CDN troubleshooting, or testing load balancer backends directly. For more DNS diagnostic tools, see our nslookup command guide.

Limit Transfer Rates

Throttle downloads or uploads to prevent saturating a shared connection:

curl --limit-rate 2m -O https://downloads.example.com/app.tar.gz

The value accepts suffixes: k for kilobytes/second, m for megabytes/second, and g for gigabytes/second.

Retry Transient Failures

Automatically retry failed downloads with a delay between attempts:

curl --retry 5 --retry-delay 2 --retry-connrefused \
     -O https://downloads.example.com/tool.tar.gz

This retries up to five times, waits two seconds between attempts, and also retries when the connection is refused. The --retry-connrefused flag is useful with rate-limited mirrors or services that restart briefly during deployment windows.

Request Compressed Responses

Ask the server for compressed content to save bandwidth on large API responses:

curl --compressed https://api.example.com/metrics

cURL negotiates the encoding automatically (gzip, brotli, or zstd depending on your build) and decompresses the response transparently.

Negotiate HTTP Versions

Force a specific HTTP protocol version when testing server capabilities:

# HTTP/2 (widely supported)
curl --http2 -I https://www.example.com

# HTTP/3 (requires cURL built with nghttp3/ngtcp2)
curl --http3 -I https://www.example.com

Most distribution packages do not include HTTP/3 support. Arch Linux is a notable exception. If --http3 returns “the installed libcurl version doesn’t support this,” your cURL build lacks the required nghttp3 and ngtcp2 libraries.

Set Connection Timeouts

Prevent scripts from hanging on slow or unresponsive endpoints by capping connection and total execution time:

curl --connect-timeout 5 --max-time 20 https://api.example.com/v1/health

This allows five seconds to establish the TCP/TLS connection and 20 seconds total before cURL exits with a timeout error. Use these flags in all production scripts to avoid indefinite hangs.

Use cURL for Non-HTTP Protocols

cURL supports dozens of protocols beyond HTTP. This makes it useful for scripting cross-protocol workflows or interacting with legacy services.

Interact with FTP Servers

List directories or transfer files on legacy FTP endpoints:

# List a remote directory
curl -u user:pass ftp://ftp.example.com/public/

# Download a file
curl -u user:pass -O ftp://ftp.example.com/public/report.csv

# Upload a file
curl -u user:pass -T backup.tar.gz ftp://ftp.example.com/uploads/

For encrypted FTP, use ftps:// for implicit FTPS (typically port 990) or add --ssl to attempt a TLS upgrade on a standard ftp:// connection. cURL falls back to plain FTP if the server does not support TLS; use --ssl-reqd instead to force encryption and fail if the server cannot negotiate TLS:

curl --ssl -u user:pass -O ftp://ftp.example.com/public/report.csv

Send Email over SMTP

Automate status notifications or alerts by sending an email through an SMTP server:

curl --url smtp://smtp.example.com \
     --mail-from sender@example.com \
     --mail-rcpt ops@example.com \
     -T email.txt \
     -u sender@example.com:password

Use smtps:// for TLS-encrypted connections and rotate credentials regularly. The email.txt file should contain the full email content including headers (From, To, Subject) and the message body.

Use Persistent cURL Settings with .curlrc

If you find yourself repeating the same flags across many cURL commands, store default options in ~/.curlrc (the cURL configuration file). cURL reads this file automatically on every invocation:

# ~/.curlrc - default cURL settings
--connect-timeout 10
--max-time 60
--retry 3
--location
--user-agent "my-cli-tool/1.0"

Any flag you place in this file applies to every cURL command unless explicitly overridden. Use curl -q to skip reading the config file when you need a clean invocation. See the official cURL documentation for the full list of configurable options.

Troubleshoot Common cURL Errors

cURL returns specific exit codes and error messages for different failure conditions. This section covers the errors you are most likely to encounter and how to resolve each one.

Could Not Resolve Host

If you see this error:

curl: (6) Could not resolve host: api.example.com

The DNS resolver cannot translate the hostname to an IP address. Check your DNS configuration or test with a known resolver:

# Test DNS resolution directly
nslookup api.example.com

# Try the request using a specific DNS server via --resolve
curl --resolve api.example.com:443:192.0.2.15 https://api.example.com/health

Common causes include incorrect hostnames, missing DNS entries, network connectivity issues, or a misconfigured /etc/resolv.conf. For a detailed walkthrough, see how to fix cURL “Could not resolve host”.

Connection Refused

If you see this error:

curl: (7) Failed to connect to api.example.com port 443 after 0 ms: Connection refused

The target server is reachable but nothing is listening on the specified port. Verify the service is running and listening on the expected port:

# Check if the port is open locally
ss -tlnp | grep 443

# Test connectivity from the client side
curl -v https://api.example.com/health 2>&1 | grep "Connected\|refused"

If the service runs on a non-standard port, include it explicitly in the URL (for example, https://api.example.com:8443/health).

SSL Certificate Problem

If you see this error:

curl: (60) SSL certificate problem: unable to get local issuer certificate

cURL cannot verify the server’s SSL/TLS certificate against your local certificate store. This often happens with self-signed certificates, expired certificates, or missing intermediate CA certificates. Diagnose the issue:

# View the certificate chain details
curl -vI https://api.example.com 2>&1 | grep -E "issuer|subject|expire"

# If the server uses a private CA, point cURL to its certificate
curl --cacert /path/to/ca-bundle.crt https://api.example.com/health

On some minimal systems, the system CA bundle may be missing entirely. Install it with your package manager (ca-certificates on most distributions).

Operation Timed Out

If you see this error:

curl: (28) Connection timed out after 5001 milliseconds

The server did not respond within the allowed time. This can be caused by network issues, firewall rules blocking the port, or an overloaded server. Increase the timeout or check connectivity:

# Increase the timeout
curl --connect-timeout 30 --max-time 120 https://api.example.com/health

# Check if the host and port are reachable
curl -v --connect-timeout 5 https://api.example.com/health 2>&1 | head -5

Protocol Not Supported

If you see this error:

curl: (1) Protocol "sftp" not supported or disabled in libcurl

Your cURL build does not include support for the requested protocol. Check which protocols your installation supports:

curl --version | grep Protocols

This is common on RHEL 9-based systems (CentOS Stream 9, AlmaLinux 9, Rocky Linux 9) that ship curl-minimal with only basic protocol support. Install the full curl package with sudo dnf swap curl-minimal curl to gain access to all protocols.

cURL Command FAQ

What is the cURL command used for?

cURL is a command-line tool for transferring data using URLs. It supports over 25 protocols including HTTP, HTTPS, FTP, SFTP, SMTP, and SCP. Common uses include downloading files, testing REST APIs, automating file transfers in scripts, debugging HTTP requests, and sending data to web services.

What is the difference between cURL and wget?

cURL supports more protocols (HTTP, FTP, SMTP, SCP, LDAP, and others) and is designed for single-shot transfers and API interaction. wget specializes in HTTP/HTTPS/FTP downloads with built-in recursive downloading and automatic retry. Use cURL for API calls, uploads, and multi-protocol work; use wget for mirroring websites or downloading entire directories.

Why does “dnf install curl” fail on CentOS Stream 9 or Rocky Linux 9?

RHEL 9-based distributions ship curl-minimal instead of full curl. The two packages conflict, so dnf install curl fails. Run sudo dnf swap curl-minimal curl to replace curl-minimal with the full curl package in a single transaction.

How do I fix “curl: command not found”?

Install cURL using your distribution’s package manager: sudo apt install curl on Ubuntu/Debian, sudo dnf install curl on Fedora, sudo pacman -S curl on Arch Linux, or sudo apk add curl on Alpine. On RHEL 9-based systems, use sudo dnf swap curl-minimal curl if the regular install fails.

Conclusion

The cURL command gives you a single tool that downloads files, shapes API requests, authenticates against secured endpoints, routes traffic through proxies, and transfers data across protocols beyond HTTP. Keep the syntax pattern, the --json shortcut, and the -w timing format variables in your notes or scripts to move faster across infrastructure work, staging environments, and production debugging.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffee Buy me a coffee

Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<a href="URL">link</a> link
<blockquote>quote</blockquote> quote block

Leave a Comment

We read and reply to every comment - let us know how we can help or improve this guide.

Let us know you are human: