Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

curl: query HTTP and network resources

By updated

Improve this page

curl is the most widespread data transfer tool in the DevOps ecosystem, installed by default on almost every Linux distribution. It is used to test a REST API in one line, download files, automate webhooks or debug network problems. Unlike Postman, curl runs directly in a terminal or a shell script, which makes it essential in CI/CD pipelines and system administration.

What you will learn

  • Send requests: GET, POST, PUT and DELETE, with headers and a JSON body
  • Download and upload files, over HTTP as well as FTP
  • Authenticate a request: Basic, Bearer token, -K configuration file
  • Diagnose an exchange with -v and the --write-out metrics
  • Harden curl in a script: --fail, --silent, configuration files

curl cheat sheet

This table gathers the options you will use day to day, to keep at hand for quick access. Each one is detailed below with a runnable example. Two landmarks to read it: options in upper and lower case do not do the same thing (-o names the file, -O keeps the remote name), and the last two rows (-w, --silent --fail) are the ones that make curl reliable in a script.

CommandDescription
curl URLSimple GET request
curl -o fichier URLDownload into a named file
curl -O URLDownload keeping the remote name
curl -I URLShow only the HTTP headers
curl -L URLFollow redirects
curl -X POST -d "data" URLSend POST data
curl -H "Header: value" URLAdd a custom header
curl -u user:pass URLHTTP Basic authentication
curl -F "file=@path" URLUpload a file (multipart)
curl -c cookies.txt URLSave the cookies
curl -b cookies.txt URLSend the cookies
curl -w "%{http_code}" URLPrint the HTTP code
curl --silent --fail URLSilent mode for scripts

What curl can do

Curl (short for Client URL) is a command-line tool designed to transfer data to or from a server, using protocols such as HTTP, HTTPS, FTP, SFTP, SCP, LDAP or SMTP.

It is used as much to:

  • Download a file from the Internet,
  • Test the endpoints of a REST API,
  • Submit HTML forms,
  • Automate file transfers,
  • Run complex HTTP requests with headers, authentication or cookies.

Unlike heavier tools such as Postman, curl is used directly from a terminal, in scripts, or inside DevOps pipelines. Its lightness and its multi-protocol support make it a remarkably effective diagnostic and automation tool, whether you are a developer or a system administrator.

Installation

curl is almost always already present on a Linux distribution. The first two tabs install the repository version of your distribution, more than enough for everyday use. The third tab only concerns the case where you need a recent feature missing from the packaged version: it builds 8.22.0 from the official sources, in your home directory so as not to overwrite the system binary. Before building, it verifies the GPG signature of the archive: gpg --verify must print Good signature from "Daniel Stenberg" and the fingerprint 27ED EAF2 2F3A BCEB 50DB 9A12 5CC9 08FD B71E 12C2 (checked on 2026-10-01). Without libpsl-dev, ./configure stops on libpsl libs and/or directories were not found (observed on Debian 13).

Terminal window
# Version du dépôt (généralement plus ancienne)
sudo apt update && sudo apt install -y curl
# Vérifier la version
curl --version

Check the installation:

Terminal window
curl --version
Output
curl 8.22.0 (x86_64-pc-linux-gnu) libcurl/8.22.0 OpenSSL/3.5.7 zlib/1.3.1 libpsl/0.21.2 nghttp2/1.64.0
Release-Date: 2026-09-02
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns mqtt mqtts pop3 pop3s rtsp smtp smtps telnet tftp ws wss
Features: alt-svc AsynchDNS HSTS HTTP2 HTTPS-proxy IPv6 Largefile libz PSL SSL threadsafe UnixSockets

Basic syntax

The curl command relies on a simple and direct structure. In its minimal form, it is made of a URL, but its power comes from the many options you can add as needed.

Terminal window
curl [options] [URL]

Essential options

Here are the most used options to get started, each with the command that shows it at work. The lowercase -o and the uppercase -O are the first pair to tell apart.

  • -o fichier: saves the content in a local file named fichier.

    Terminal window
    curl -o page.html https://example.com
  • -O: downloads the file keeping its original name on the server.

    Terminal window
    curl -O https://example.com/fichier.zip
  • -L: follows HTTP redirects automatically (very useful on shortened or moved URLs).

    Terminal window
    curl -L https://example.com/ancien-lien
  • -I: shows only the HTTP headers.

    Terminal window
    curl -I https://example.com
  • -X: sets an HTTP method (GET, POST, PUT, DELETE…).

    Terminal window
    curl -X DELETE https://api.example.com/ressource
  • -d: sends data in the request body (POST by default).

    Terminal window
    curl -d "nom=Jean&age=30" https://example.com/formulaire
  • -H: adds a custom header (e.g. JSON, token, cookie...).

    Terminal window
    curl -H "Content-Type: application/json" https://api.example.com
  • -u: uses HTTP Basic authentication.

    Terminal window
    curl -u admin:motdepasse https://example.com/admin

Common uses

One of the most frequent uses of curl is downloading content from a URL. It can be HTML pages, archives, images or scripts. Here is how to do it efficiently.

Saving to a specific file

To download a page or a file and choose the local name:

Terminal window
curl -o page.html https://example.com
Output
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 513 0 513 0 0 10421 0 0

This writes the content of the response into a file named page.html.

Showing only the HTTP headers

The -I option fetches the metadata without downloading the content:

Terminal window
curl -I https://example.com
Output
HTTP/2 200
date: Sat, 24 Jan 2026 08:44:59 GMT
content-type: text/html
cf-ray: 9c2e40871e88791b-CDG
last-modified: Fri, 23 Jan 2026 20:51:32 GMT
allow: GET, HEAD
accept-ranges: bytes
age: 3591
cf-cache-status: HIT
server: cloudflare

Keeping the remote file name

If you want to fetch a file while keeping its original name (taken from the URL):

Terminal window
curl -O https://example.com/script.sh

This creates a script.sh file in the current directory.

Following redirects

Some links redirect you automatically (HTTP 301 or 302). Without any option, curl does not follow them. You must add -L:

Terminal window
curl -LO https://bit.ly/exemple-lien

This lets you reach the final URL and fetch the right content. Watch the name: -O takes it from the URL you typed, not from the final URL. Here, the file is therefore called exemple-lien. Force the name with -o, or add -J when the server announces a name in the Content-Disposition header.

Resuming an interrupted download

curl can resume a download if the connection is interrupted:

Terminal window
curl -C - -O https://example.com/long-fichier.zip

The -C - option detects the size of the existing file and continues from there.

Downloading several files

Use several -O options:

Terminal window
curl -O https://site1.com/fichier1.txt -O https://site2.com/fichier2.jpg

Or use a shell loop to automate it, reading the file line by line: a for url in $(cat urls.txt) form would split on spaces and expand wildcards such as * or ? present in a URL.

Terminal window
while read -r url; do curl -O "$url"; done < urls.txt

Sending HTTP requests

curl is also a powerful tool to interact with REST APIs. It can send every kind of HTTP request: GET, POST, PUT, DELETE, with data, headers or authentication. Responses almost always come in JSON format (in French), which you can filter with jq.

GET request (default)

The GET method is the one used by default. It fetches data without sending any.

Terminal window
curl -s https://httpbin.org/get
Output
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"User-Agent": "curl/8.22.0",
"X-Amzn-Trace-Id": "Root=1-69748697-791d1c236921ff1653569139"
},
"origin": "86.229.137.201",
"url": "https://httpbin.org/get"
}

POST request with simple data

Send application/x-www-form-urlencoded data:

Terminal window
curl -X POST -d "nom=Alice&age=28" https://api.example.com/inscription

By default, -d uses POST, so -X POST is optional here.

POST request with JSON

Sending a JSON payload requires setting the Content-Type header:

Terminal window
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"1234"}' \
https://httpbin.org/post
Output
{
"args": {},
"data": "{\"email\":\"test@example.com\",\"password\":\"1234\"}",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Content-Length": "46",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "curl/8.22.0"
},
"json": {
"email": "test@example.com",
"password": "1234"
},
"origin": "86.229.137.201",
"url": "https://httpbin.org/post"
}

Sending data from a file

Rather than including the JSON inline, use @ to read from a file:

Terminal window
echo '{"nom":"Jean","age":30}' > data.json
curl -s -X POST \
-H "Content-Type: application/json" \
-d @data.json \
https://httpbin.org/post
Output
{
"data": "{\"nom\":\"Jean\",\"age\":30}",
"json": {
"age": 30,
"nom": "Jean"
}
}

PUT or DELETE request

Simulate modification or deletion requests:

Terminal window
curl -s -X PUT \
-H "Content-Type: application/json" \
-d '{"status":"actif"}' \
https://httpbin.org/put
Output
{
"data": "{\"status\":\"actif\"}",
"json": {
"status": "actif"
}
}
Terminal window
curl -s -X DELETE https://httpbin.org/delete
Output
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"User-Agent": "curl/8.22.0"
},
"json": null
}

Sending several headers

You can send several custom HTTP headers:

Terminal window
curl -s \
-H "Authorization: Bearer mon_token_secret" \
-H "Accept: application/json" \
https://httpbin.org/headers
Output
{
"headers": {
"Accept": "application/json",
"Authorization": "Bearer mon_token_secret",
"Host": "httpbin.org",
"User-Agent": "curl/8.22.0"
}
}

Authentication

Many APIs or servers require authentication to access their resources. curl supports several methods: HTTP Basic, Bearer token, or FTP/SFTP authentication.

HTTP Basic authentication

It is the simplest form, where you send a user name and a password encoded in base64:

Terminal window
curl -s -u testuser:testpass https://httpbin.org/basic-auth/testuser/testpass
Output
{
"authenticated": true,
"user": "testuser"
}

This method works for APIs or administration interfaces protected by a classic login window in the browser.

Authentication through a Bearer header (token)

Many modern APIs use an authentication token (JWT or other) to include in the Authorization HTTP header:

Terminal window
curl -H "Authorization: Bearer VOTRE_TOKEN" https://api.example.com/profil

This kind of token is often obtained through a POST request in exchange for credentials (OAuth flow or custom authentication).

Authentication with configuration files

To avoid typing your credentials in clear text or using tokens on the command line, curl can store options in a file:

Example: curl.conf

Terminal window
--user utilisateur:motdepasse
--url https://api.example.com/securise

And to use it:

Terminal window
chmod 600 curl.conf # le fichier contient un secret
curl -K curl.conf

The password no longer goes through the command line, so neither through the shell history nor through ps aux. In return, it lives in clear text in the file: restrict reading it to your account and never commit it to a Git repository.

FTP or SFTP authentication

curl can also authenticate to an FTP server:

Terminal window
curl -u ftpuser:ftppass ftp://ftp.example.com/fichier.txt

Or with SFTP:

Terminal window
curl -u user:sftpmdp sftp://serveur.example.com/dossier/

SFTP and SCP depend on how curl was built. The Debian 13 package supports them, the one from AlmaLinux 10 only handles file, ftp, ftps, http, https, ipfs and ipns, and answers curl: (1) Protocol "sftp" not supported. Check the Protocols line of curl --version before building a script on it.

Using FTP/SFTP

The curl command can also transfer files over the FTP and SFTP protocols, for reading as well as writing. It is very useful to automate transfers to remote servers, backups or publications.

Downloading a file over FTP

To fetch a file from an FTP server:

Terminal window
curl -u utilisateur:motdepasse ftp://ftp.example.com/fichier.txt -O
  • -u: provides the credentials
  • ftp://: sets the protocol
  • -O: keeps the original file name

Uploading a file over FTP

To send a file to an FTP server:

Terminal window
curl -T monfichier.txt -u utilisateur:motdepasse ftp://ftp.example.com/
  • -T: gives the file to upload

Uploading a file over SFTP

Same principle as FTP, but with a secure protocol:

Terminal window
curl -T fichier.txt -u user:motdepasse sftp://sftp.example.com/chemin/

Listing the content of an FTP directory

To get the list of files of a remote directory:

Terminal window
curl -u utilisateur:motdepasse ftp://ftp.example.com/

This returns a raw view of the content of the target directory.

Adding a custom port

If the server does not use the standard ports:

Terminal window
curl -u user:pass ftp://ftp.example.com:2121/fichier.txt -O

Uploading files

With curl, you can also upload files to a server, in particular through HTML forms (as multipart/form-data) or through FTP/SFTP.

Uploading a file with POST (multipart/form-data)

To simulate sending a file from an HTML form:

Terminal window
echo "fichier test upload" > upload.txt
curl -s -F "file=@upload.txt" https://httpbin.org/post
Output
{
"files": {
"file": "fichier test upload\n"
},
"headers": {
"Content-Type": "multipart/form-data; boundary=------------------------ym8RxeZV33VnKHabQKinXP",
"User-Agent": "curl/8.22.0"
}
}
  • -F: sends the data as a form
  • @: indicates that the content of the file must be sent

Uploading several files in a single request

You can send several fields, including several files:

Terminal window
curl -F "photo1=@image1.jpg" -F "photo2=@image2.jpg" https://example.com/galerie

And also add text fields at the same time:

Terminal window
curl -F "titre=Vacances" -F "photo=@plage.jpg" https://example.com/album

Uploading through PUT (REST API)

Some APIs accept files sent with a PUT method:

Terminal window
curl -T script.sh -X PUT https://api.example.com/scripts/

In HTTP, -T already sends a PUT: the -X PUT is redundant. And since the URL ends with /, curl appends the file name to it, so the request goes to /scripts/script.sh.

You can also set a specific MIME header:

Terminal window
curl -T data.csv -H "Content-Type: text/csv" https://api.example.com/donnees

Advanced use

Beyond simple requests, curl offers powerful options for more complex uses: proxy, user-agent, cookies, debug, configuration, etc.

Using a proxy

To send a request through an HTTP proxy server:

Terminal window
curl -x http://proxy.mondomaine.local:3128 https://example.com

And for a proxy with authentication:

Terminal window
curl -x http://proxy:port -U utilisateur:motdepasse https://example.com

Setting a custom User-Agent

Some servers answer differently depending on the browser simulated. To change the client identifier:

Terminal window
curl -s -A "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" \
https://httpbin.org/user-agent
Output
{
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
}

Handling cookies

To test a site that keeps a session, curl can save the cookies received in a file, then send them back on the next request. Two options answer each other: -c (cookie jar) writes the received cookies, -b reads them back and sends them. The sequence below chains both to replay a session in full.

  1. Save the cookies to a file:

    Terminal window
    curl -s -c cookies.txt -L https://httpbin.org/cookies/set/session/abc123
  2. Check the cookies file:

    Terminal window
    cat cookies.txt
    Output
    # Netscape HTTP Cookie File
    httpbin.org FALSE / FALSE 0 session abc123
  3. Reuse the cookies:

    Terminal window
    curl -s -b cookies.txt https://httpbin.org/cookies
    Output
    {
    "cookies": {
    "session": "abc123"
    }
    }

Showing request metrics

The -w option (write-out) extracts metrics:

Terminal window
curl -s -w "\n--- Métriques ---\nCode HTTP: %{http_code}\nTemps total: %{time_total}s\nTaille: %{size_download} octets\n" \
-o /dev/null https://example.com
Output
--- Métriques ---
Code HTTP: 200
Temps total: 0.074715s
Taille: 513 octets

Limiting the bandwidth

Simulate a slow connection with --limit-rate:

Terminal window
curl --limit-rate 100k -O https://example.com/film.mp4

Using a configuration file

You can save curl options in a text file:

options.conf:

--user utilisateur:mdp
--header "Accept: application/json"
--url https://api.example.com/data

And run:

Terminal window
curl -K options.conf

Good practices

To get the most out of curl, adopt these habits that make your commands safer, more readable and more reusable.

Always check the installed version

Some features may not be available in older versions:

Terminal window
curl --version

This also gives you the supported protocols (HTTP/2, SSL, etc.).

Using --silent for automated scripts

To avoid noise in scripts (progress bars or minor errors):

Terminal window
curl --silent --fail --show-error -o fichier.txt https://example.com
  • --silent: removes the progress bar and the error messages, not the downloaded content
  • --fail: returns exit code 22 if the HTTP code is ≥ 400
  • --show-error: shows again the error messages hidden by --silent

Preferring configuration files for long commands

Rather than typing a 5-line command, put the options in a curl.conf file and run:

Terminal window
curl -K curl.conf

It is more readable, versionable and easy to review, as long as the file contains no secret. Credentials go into a second file in chmod 600, outside the repository, or go through standard input as described in the next section.

Always setting headers explicitly for APIs

Many APIs require Content-Type: application/json. Be explicit:

Terminal window
curl -H "Content-Type: application/json" ...

Never exposing passwords in shared scripts

Avoid hard-coded credentials in a script, but also beware of the false good idea of variables: curl -u "$API_USER:$API_PASS" is expanded by the shell before the call, and the secret ends up in the process arguments. curl tries to blank the -u value once started, but a ps run at the same moment still reads it, and a header passed with -H is never blanked. The safe form routes the secret through standard input, which -K - reads as a configuration file:

Terminal window
printf 'user = "%s:%s"\n' "$API_USER" "$API_PASS" | curl -sS -K - https://api.example.com/securise
printf 'header = "Authorization: Bearer %s"\n' "$API_TOKEN" | curl -sS -K - https://api.example.com/profil

printf is a shell builtin: no process carries the secret as an argument. Measured with curl 8.18.0, the server does receive the Authorization header in both cases. Finally, avoid the name $USER for your variables: the shell already fills it with your login name, and that is what would be sent to the server.

Troubleshooting

curl errors fall into two families that are easy to tell apart. The codes in parentheses (curl: (6), (7), (35)...) are curl errors: the transfer did not complete, the problem lies on the network, DNS or TLS side. The 4xx codes, on the other hand, mean that the request did arrive but that the server refused it: the exchange works, it is authentication or permissions that block. Identifying the family points straight to the right column.

ProblemProbable causeFix
curl: (6) Could not resolve hostDNS or wrong domain nameCheck the URL and the network connection
curl: (7) Failed to connectClosed port or firewallCheck that the service is listening
curl: (35) TLS connect errorTLS negotiation impossible (version or cipher suite refused)Compare the TLS versions accepted on both sides
curl: (60) SSL certificate ... verify resultExpired, self-signed certificate or unknown authorityRenew the certificate, update ca-certificates, or --cacert; --insecure in dev only
curl: (60) ... no alternative certificate subject name matchesThe URL name is not in the certificateCheck the names (SAN) of the certificate
curl: (56) Recv failureConnection interruptedRetry with -C - to resume
HTTP 401 UnauthorizedInvalid token or credentialsCheck -u or the Authorization header
HTTP 403 ForbiddenInsufficient permissionsCheck the access rights
HTTP 429 Too Many RequestsRate limitingAdd a delay between requests

Key points

  • curl transfers data over about thirty protocols depending on how it was built (29 on Debian 13, 7 on AlmaLinux 10)
  • The -o option saves to a file, -O keeps the remote name
  • -I shows the HTTP headers, -L follows redirects
  • For APIs: -X sets the method, -H adds headers, -d sends data
  • -u user:pass for Basic auth, -H "Authorization: Bearer TOKEN" for tokens
  • -F "file=@chemin" uploads files as multipart
  • --silent --fail --show-error for use in scripts
  • Keep curl up to date: 8.18.0 fixed 6 CVEs, 8.22.0 fixes 9 more
  • Pass secrets through a -K file in chmod 600 or through standard input (-K -), never as a command argument

Knowledge check

Check that the essentials of this guide are acquired. The questions only cover what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

6 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Resources

These links lead to the official documentation of the curl project, the reference to check an option or the list of security advisories.

FAQ: frequently asked questions

The answers below cover authentication, proxies and the security of credentials in scripts.

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn