Salta al contenuto principale

curl: scarica e interroga siti e servizi web

Inviato da tuxsa il

curl permette di fare dal terminale ciò che di solito fa un browser: chiedere una pagina a un server, scaricare un file, inviare dati a un servizio. Serve a controllare se un sito risponde, a provare un'API, a verificare un reindirizzamento o un certificato, e a scaricare file negli script.

Cos'è

curl sta per client URL: un programma "cliente" che lavora a partire da un indirizzo (URL). È un progetto libero, in costante sviluppo da oltre vent'anni, e la sua libreria libcurl è usata da moltissimi altri programmi. Oltre a HTTP e HTTPS parla altri protocolli (FTP, SFTP, SCP, SMTP e molti altri), ma nell'uso quotidiano si tratta quasi sempre di web. Su Debian e Ubuntu è nel pacchetto curl, che nelle installazioni minime può mancare: sudo apt install curl.

Prima di cominciare: richiesta e risposta HTTP

Per usare bene curl serve un'idea di come dialoga il web. Il client invia una richiesta, il server restituisce una risposta composta da:

  • un codice di stato numerico, che dice com'è andata;
  • delle intestazioni (headers), informazioni sulla risposta: tipo di contenuto, data, server, eventuali reindirizzamenti;
  • il corpo (body), cioè il contenuto vero e proprio: la pagina HTML, un file, dei dati JSON.
CodiceSignificato
200Tutto bene
301, 302Reindirizzamento permanente o temporaneo: il contenuto è altrove (campo Location)
304Non modificato (contenuto già in cache)
401, 403Serve autenticazione / accesso vietato
404Non trovato
429Troppe richieste
500, 502, 503Errore del server / del servizio a monte / servizio non disponibile

Cosa fa

Invia la richiesta all'indirizzo indicato e stampa il corpo della risposta sullo standard output, cioè a schermo. Non salva nulla su disco a meno che tu non lo chieda con -o o -O. Non segue i reindirizzamenti, non esegue JavaScript e non mostra le intestazioni, se non glielo chiedi.

Come si usa

$ curl [opzioni] URL

Gli indirizzi negli esempi (example.org, miosito.example) sono d'esempio: sostituiscili con i tuoi. Un consiglio per tutto l'articolo: scrivi gli URL tra virgolette, perché caratteri come &, ? e [ hanno un significato per la shell.

Esempio 1: leggere una pagina

$ curl https://example.org | head -n 5
<!doctype html>
<html>
<head>
    <title>Example Domain</title>
    ...

Il corpo della risposta (qui l'HTML della pagina) arriva sullo standard output, e quindi si può passare ad altri comandi con la pipe, come head, grep o wc.

Esempio 2: salvare in un file con -o e -O

$ curl -o pagina.html https://example.org
$ curl -O https://example.org/archivio.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 2048k  100 2048k    0     0  5120k      0 --:--:-- --:--:-- --:--:-- 5120k

Con -o nome scegli tu il nome del file; con -O (maiuscola) usa il nome presente nell'indirizzo, come archivio.tar.gz. La tabella è l'indicatore di avanzamento, che compare solo quando l'output non va a schermo. Un archivio scaricato così si può poi estrarre con tar.

Esempio 3: seguire i reindirizzamenti con -L

$ curl http://miosito.example
<html><head><title>301 Moved Permanently</title>...
$ curl -L http://miosito.example

Molti siti rispondono con un reindirizzamento (per esempio da http a https, o dal vecchio al nuovo indirizzo). Senza -L (location) curl si ferma e ti mostra la pagina del reindirizzamento. Con -L lo segue fino alla destinazione finale.

Esempio 4: vedere solo le intestazioni con -I

$ curl -I https://example.org
HTTP/2 200
date: Sun, 18 Oct 2026 04:35:12 GMT
content-type: text/html; charset=UTF-8
content-length: 1256
server: nginx

Con -I (maiuscola) curl invia una richiesta di tipo HEAD e mostra solo codice di stato e intestazioni, senza scaricare il corpo. È il modo più rapido per controllare che un sito risponda. Con -i (minuscola) vengono mostrate le intestazioni e il corpo.

Esempio 5: controllare un reindirizzamento (per esempio dopo una migrazione)

$ curl -sIL http://vecchio.example | grep -iE '^(HTTP|location)'
HTTP/1.1 301 Moved Permanently
Location: https://www.nuovo.example/
HTTP/2 200

Con -s (silenzioso) si toglie l'indicatore di avanzamento, con -IL si seguono i reindirizzamenti mostrando solo le intestazioni, e grep tiene soltanto le righe del codice di stato e della destinazione. È utile, per esempio, per verificare che i reindirizzamenti 301 dopo il cambio di dominio di un sito siano quelli attesi.

Esempio 6: solo il codice di stato e i tempi con -w

$ curl -s -o /dev/null -w "%{http_code}\n" https://example.org
200
$ curl -s -o /dev/null -w "codice: %{http_code}\ntempo totale: %{time_total}s\n" https://example.org
codice: 200
tempo totale: 0.231542s

Con -o /dev/null si scarta il corpo, e con -w (write-out) si stampa un messaggio personalizzato con variabili tra %{...}. Le più utili: http_code (il codice), time_total (durata complessiva), time_namelookup (tempo del DNS), time_connect (tempo per collegarsi), time_starttransfer (attesa del primo byte), size_download (byte ricevuti) e, con -L, url_effective (indirizzo finale dopo i reindirizzamenti).

Esempio 7: nessun rumore, ma gli errori sì: -sS e -f

$ curl -sS https://example.org/inesistente
<html><head><title>404 Not Found</title>...
$ curl -sSf https://example.org/inesistente
curl: (22) The requested URL returned error: 404

Con -s curl tace anche in caso di errore; aggiungendo -S mostra almeno i messaggi d'errore. Con -f (fail), in caso di errore HTTP (404, 500...) non stampa il corpo e restituisce un codice di uscita diverso da zero. Senza -f, un 404 per curl è comunque una risposta ricevuta con successo, e il codice di uscita sarà 0. La combinazione -sSf è quella da usare negli script.

Esempio 8: inviare dati di un modulo con -d

$ curl -d "nome=Enzo&citta=Salerno" https://miosito.example/iscrizione
$ curl --data-urlencode "messaggio=Ciao, città!" https://miosito.example/iscrizione

Con -d curl invia i dati nel corpo di una richiesta POST, come farebbe un modulo web (codifica application/x-www-form-urlencoded). Non serve aggiungere -X POST: -d lo implica già. Per testo con spazi o caratteri speciali si usa --data-urlencode, che li codifica correttamente.

Esempio 9: inviare e ricevere JSON

$ curl -s -X POST https://miosito.example/api/messaggi \
    -H "Content-Type: application/json" \
    -d '{"titolo": "Prova", "testo": "Ciao dal terminale"}'
$ curl -s -d @messaggio.json -H "Content-Type: application/json" https://miosito.example/api/messaggi

Per le API bisogna dire al server che i dati sono in formato JSON, con l'intestazione Content-Type impostata con -H. I dati si scrivono tra apici singoli, in modo che le virgolette interne non vengano toccate dalla shell, oppure si leggono da un file con @nomefile. Lo stesso schema serve a provare un webhook di uno strumento di automazione come n8n: curl -X POST URL_DEL_WEBHOOK -H "Content-Type: application/json" -d '{"prova": true}'. L'opzione -X serve quando il metodo non è GET né POST: -X PUT, -X PATCH, -X DELETE.

Esempio 10: leggere i dati di un'API

$ curl -s "https://miosito.example/jsonapi/node/article?page[limit]=3" -g \
    -H "Accept: application/vnd.api+json" | head -c 300

Con -H "Accept: ..." si chiede al servizio il formato desiderato. L'opzione -g (globoff) è qui indispensabile: curl interpreta le parentesi quadre come un intervallo da espandere (vedi più avanti), e il parametro page[limit]=3, tipico di API come JSON:API di Drupal, darebbe un errore. Per leggere comodamente il risultato si può passare l'output a jq (sudo apt install jq), che lo formatta e permette di estrarre singoli campi.

Esempio 11: autenticazione e intestazioni

$ curl -u enzo https://miosito.example/area-riservata
Enter host password for user 'enzo':
$ curl -H "Authorization: Bearer $TOKEN" https://miosito.example/api/dati
$ curl -A "Mozilla/5.0" -e "https://miosito.example/" https://miosito.example/pagina

Con -u utente si usa l'autenticazione di base: se non scrivi la password, curl la chiede, ed è la forma da preferire. Molte API usano un token, inviato nell'intestazione Authorization; qui è preso da una variabile d'ambiente. Con -A si imposta l'identificativo del programma (User-Agent) e con -e la pagina di provenienza (Referer).

Esempio 12: caricare un file

$ curl -F "file=@foto.jpg" -F "descrizione=Tramonto a Salerno" https://miosito.example/carica
$ curl -T backup.tar.gz https://miosito.example/archivio/backup.tar.gz

Con -F si invia un modulo con allegati (multipart, come il pulsante "scegli file" di una pagina web), indicando i file con @. Con -T si carica un file con una richiesta PUT sul percorso indicato, se il server lo prevede.

Esempio 13: scaricare più file e gli intervalli

$ curl -O https://example.org/a.pdf -O https://example.org/b.pdf
$ curl -O "https://example.org/foto[1-3].jpg"
$ curl -O "https://example.org/{doc,note}.txt"

Si possono indicare più indirizzi, ciascuno con il suo -O. curl sa anche espandere da solo intervalli tra parentesi quadre ([1-3] scarica foto1.jpg, foto2.jpg e foto3.jpg) ed elenchi tra graffe ({doc,note}), e per questo l'indirizzo va messo tra virgolette, altrimenti la shell interverrebbe prima.

Esempio 14: download robusti: tentativi, tempi e ripresa

$ curl -fL --retry 3 --connect-timeout 5 -m 60 -O https://example.org/grande.iso
$ curl -C - -O https://example.org/grande.iso
$ curl --limit-rate 1m -O https://example.org/grande.iso

Con --retry 3 in caso di errori temporanei curl riprova fino a tre volte; --connect-timeout 5 rinuncia se non riesce a collegarsi entro 5 secondi; -m 60 (max-time) interrompe tutto dopo 60 secondi. Se un download si interrompe, -C - lo riprende da dove era arrivato. Con --limit-rate si limita la velocità, per non saturare la connessione.

Esempio 15: provare un server prima di cambiare il DNS

$ curl -I --resolve miosito.example:443:203.0.113.10 https://miosito.example
$ curl -I -H "Host: miosito.example" http://203.0.113.10

Con --resolve nome:porta:indirizzo si dice a curl di usare un indirizzo scelto da te per quel nome, ignorando il DNS. Così, durante una migrazione, puoi verificare che il nuovo server risponda correttamente (certificato compreso) prima di spostare il dominio. La seconda forma invia la richiesta all'indirizzo IP specificando a mano il nome del sito, ma non funziona con HTTPS, perché il certificato verrebbe controllato sull'indirizzo e non sul nome.

Esempio 16: controllare la scadenza del certificato

$ curl -vI https://example.org 2>&1 | grep -E 'subject:|expire date:'
*  subject: CN=example.org
*  expire date: Nov 20 23:59:59 2026 GMT

Con -v (verbose) curl mostra tutti i dettagli della comunicazione, comprese le informazioni sul certificato, e le invia al canale degli errori. Per questo serve 2>&1 prima di passare il tutto a grep. Un controllo veloce per non farsi sorprendere da un certificato scaduto.

Esempio 17: copiare una richiesta dal browser

Nel browser, apri gli strumenti per sviluppatori (F12), scheda "Rete", clicca col tasto destro su una richiesta e scegli Copia come cURL. Ottieni il comando completo di intestazioni e dati, che puoi incollare nel terminale e rieseguire: è il modo più rapido per riprodurre e analizzare ciò che fa una pagina.

Esempio 18: il tuo indirizzo IP pubblico

$ curl -s https://ifconfig.me
203.0.113.25

Servizi come questo rispondono con l'indirizzo da cui arriva la richiesta. Sei però comunque in contatto con un servizio esterno: usalo con questa consapevolezza.

Esempio 19: uso negli script

#!/bin/bash
URL="${1:?Indica l'indirizzo da controllare}"

CODICE=$(curl -s -o /dev/null -m 10 -w "%{http_code}" "$URL")

if [ "$CODICE" = "200" ]; then
    echo "OK: $URL risponde (200)"
else
    echo "PROBLEMA: $URL ha risposto con codice $CODICE" >&2
    exit 1
fi

Se non riesce nemmeno a collegarsi (sito spento, DNS che non risponde), curl restituisce come codice 000. Lo script si può eseguire a intervalli regolari per sorvegliare un sito.

Un altro uso tipico è lo scaricamento di un file con controlli:

if curl -fsSL --retry 3 -o "$HOME/aggiornamento.tar.gz" "https://example.org/aggiornamento.tar.gz"; then
    echo "Scaricato"
else
    echo "Download fallito" >&2
    exit 1
fi

Opzioni utili

  • -o file / -O: salva in un file con un nome scelto / con il nome presente nell'indirizzo (-OJ usa il nome indicato dal server).
  • -L: segue i reindirizzamenti.
  • -I: solo le intestazioni; -i: intestazioni e corpo; -v: dettagli completi della comunicazione.
  • -s / -S: silenzioso / mostra comunque gli errori.
  • -f: considera un errore le risposte 4xx e 5xx.
  • -w formato: stampa informazioni sulla richiesta (codice, tempi, dimensioni).
  • -d dati / --data-urlencode / -F campo=valore: invia dati (modulo, dati codificati, modulo con file).
  • -X metodo: sceglie il metodo HTTP (PUT, DELETE...).
  • -H "Intestazione: valore": aggiunge un'intestazione.
  • -u utente: autenticazione di base; -A: User-Agent; -e: Referer.
  • -b / -c file: invia / salva i cookie.
  • -m secondi: durata massima; --connect-timeout: attesa massima per collegarsi; --retry N: tentativi.
  • -C -: riprende un download interrotto; --limit-rate: limita la velocità.
  • -g: disattiva l'espansione di [] e {} negli indirizzi.
  • --resolve nome:porta:ip: forza l'indirizzo di un nome.
  • -4 / -6: forza IPv4 o IPv6.

Attenzione a

  • Gli URL vanno tra virgolette: curl https://example.org/pagina?a=1&b=2 senza virgolette viene interpretato dalla shell, che con la & manda il comando in secondo piano e ignora il resto.
  • Per default non si seguono i reindirizzamenti: se ottieni una pagina strana o vuota, prova con -L (o guarda il codice con -I).
  • Un 404 non è un errore, senza -f: il codice di uscita è 0 e il corpo stampato è la pagina d'errore. Con -f il codice di uscita è 22.
  • Codici di uscita utili nei controlli: 0 tutto bene, 6 nome non risolto (DNS), 7 collegamento rifiutato o impossibile, 22 errore HTTP (con -f), 28 tempo scaduto, 35 e 60 problemi con il certificato o la connessione cifrata.
  • Non disattivare i controlli di sicurezza: l'opzione -k (--insecure) ignora gli errori di certificato e rende la connessione vulnerabile a intercettazioni. Serve al massimo per una prova su una macchina tua; per risolvere un errore di certificato, meglio capirne la causa (scaduto, nome sbagliato, catena incompleta).
  • I segreti sulla riga di comando: -u utente:password e i token scritti per esteso restano nella cronologia dei comandi (history) e sono visibili nell'elenco dei processi mentre il comando gira. Meglio far chiedere la password a curl, usare una variabile letta da un file protetto, o il file ~/.netrc con l'opzione -n.
  • Non eseguire quello che scarichi senza leggerlo: istruzioni come curl -fsSL URL | sh (o peggio | sudo bash) eseguono sul tuo computer uno script mai visto. Scaricalo in un file, leggilo e solo dopo eseguilo.
  • Con -d e un file: -d @file toglie gli a capo dal contenuto. Per inviare un file così com'è (JSON formattato, dati binari) usa --data-binary @file.
  • -X POST superfluo: con -d o -F il metodo è già POST. Forzare un metodo diverso con -X senza che serva può causare comportamenti inattesi con i reindirizzamenti.
  • Contenuto binario a schermo: se per sbaglio scrivi un archivio o un'immagine sul terminale, curl si rifiuta e avvisa. Se il terminale si riempie di simboli, scrivi reset.
  • Non esegue JavaScript: le pagine che si costruiscono nel browser possono risultare quasi vuote. Spesso i dati arrivano da un'API che si può interrogare direttamente (la scheda "Rete" del browser la rivela).
  • L'indicatore di avanzamento nei registri: se redirigi l'output di curl in un file di registro, l'indicatore di avanzamento finisce nei messaggi di errore. Usa -s (con -S per gli errori).
  • Rispetto per i servizi altrui: non inviare richieste a raffica a siti che non gestisci, rispetta i limiti e i termini d'uso, e identificati con un User-Agent chiaro quando automatizzi.
  • Proxy: curl usa le variabili d'ambiente http_proxy e https_proxy. Se le risposte sembrano strane, controllale; --noproxy '*' le ignora.
  • curl o wget? curl è il più versatile per interrogare servizi e API e per gli script; wget è più comodo per scaricare interi siti o cartelle in modo ricorsivo.

Riepilogo

ComandoCosa fa
curl URLStampa a schermo il corpo della risposta
curl -O URLScarica il file con il suo nome
curl -o nome URLScarica con un nome scelto
curl -L URLSegue i reindirizzamenti
curl -I URLSolo le intestazioni (codice di stato incluso)
curl -sIL URL | grep -iE '^(HTTP|location)'Mostra la catena dei reindirizzamenti
curl -s -o /dev/null -w "%{http_code}" URLSolo il codice di stato
curl -sSf URLSilenzioso ma con errori, fallisce sugli errori HTTP
curl -d "a=1&b=2" URLInvia dati di un modulo (POST)
curl -H "Content-Type: application/json" -d '{…}' URLInvia JSON
curl -F "file=@foto.jpg" URLCarica un file
curl -u utente URLAutenticazione con password chiesta in modo riservato
curl -C - -O URLRiprende un download interrotto
curl --resolve nome:443:IP URLProva un server prima di cambiare il DNS
curl -vI URL 2>&1 | grep 'expire date'Controlla la scadenza del certificato