Der curl-Befehl, auf den Linux-Nutzer täglich zurückgreifen, ist weit mehr als ein Download-Werkzeug — er ist ein vollständiger HTTP-Client, der REST APIs testen, Workflows automatisieren, Netzwerkprobleme debuggen und Daten über Dutzende von Protokollen übertragen kann. Ob du einen defekten Webhook fehlerbehebst, eine API-Integration skriptest oder TLS-Zertifikate prüfst: curl zu beherrschen ist eine der wirkungsvollsten Fähigkeiten, die ein Sysadmin oder Entwickler haben kann. Diese Anleitung behandelt jeden praktischen Anwendungsfall von einfachen GETs bis hin zum vollständigen CRUD-REST-API-Testing, mit realen Beispielen durchgehend.

Voraussetzungen

  • Ein Linux-System mit installiertem curl (curl --version zur Überprüfung; Installation via sudo apt install curl oder sudo dnf install curl)
  • Grundlegende Vertrautheit mit dem Terminal und HTTP-Konzepten (Anfragemethoden, Header, Statuscodes)
  • Ein API-Endpunkt zum Testen — die Beispiele verwenden https://jsonplaceholder.typicode.com, eine kostenlose öffentliche Mock-API
  • Optional: jq installiert für formatierte JSON-Ausgaben (sudo apt install jq)

HTTP-Anfragen stellen: GET, POST, PUT, DELETE

curl verwendet standardmäßig GET, der einfachste mögliche Befehl ist daher nur eine URL:

curl https://jsonplaceholder.typicode.com/posts/1

Füge -s (silent) hinzu, um die Fortschrittsanzeige in Skripten zu unterdrücken, und leite die Ausgabe an jq weiter für lesbare Ergebnisse:

curl -s https://jsonplaceholder.typicode.com/posts/1 | jq .

POST — eine Ressource erstellen:

curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '{"title":"My Post","body":"Hello world","userId":1}' \
  https://jsonplaceholder.typicode.com/posts | jq .

Das Flag -X POST setzt die Methode. -H fügt einen Header hinzu. -d sendet den Anfrage-Body. Für Formular-Übermittlungen statt JSON lasse den Content-Type-Header weg oder verwende -d "field=value&other=data".

PUT — eine Ressource aktualisieren:

curl -s -X PUT \
  -H "Content-Type: application/json" \
  -d '{"title":"Updated Title","body":"New body","userId":1}' \
  https://jsonplaceholder.typicode.com/posts/1 | jq .

PATCH — teilweise Aktualisierung:

curl -s -X PATCH \
  -H "Content-Type: application/json" \
  -d '{"title":"Just the title changed"}' \
  https://jsonplaceholder.typicode.com/posts/1 | jq .

DELETE — eine Ressource entfernen:

curl -s -X DELETE https://jsonplaceholder.typicode.com/posts/1
echo "HTTP status: $?"

Um den HTTP-Statuscode explizit zu erfassen:

curl -s -o /dev/null -w "%{http_code}" -X DELETE \
  https://jsonplaceholder.typicode.com/posts/1

Das Flag -w (write-out) mit %{http_code} gibt nur den numerischen Statuscode aus — ideal zur Prüfung von Erfolg oder Misserfolg in Shell-Skripten.

Authentifizierung: Header, Bearer-Token und Basic Auth

Die meisten Produktions-APIs erfordern eine Authentifizierung. curl unterstützt jedes gängige Schema.

Bearer-Token (OAuth 2.0 / JWT):

curl -s -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  https://api.example.com/protected-resource | jq .

HTTP Basic Auth — zwei gleichwertige Formen:

# Form 1: -u Flag (curl fügt den Authorization-Header automatisch hinzu)
curl -s -u myuser:mysecretpass https://api.example.com/data

# Form 2: expliziter Header (nützlich beim Skripten mit Variablen)
curl -s -H "Authorization: Basic $(echo -n myuser:mysecretpass | base64)" \
  https://api.example.com/data

API-Schlüssel als Query-Parameter:

curl -s "https://api.example.com/data?api_key=YOUR_KEY_HERE" | jq .

API-Schlüssel als benutzerdefinierten Header (üblich bei Diensten wie OpenAI oder Stripe):

curl -s -H "X-API-Key: YOUR_KEY_HERE" https://api.example.com/endpoint | jq .

Zugangsdaten in einer netrc-Datei speichern hält Secrets aus der Shell-History fern:

# ~/.netrc
machine api.example.com
login myuser
password mysecretpass
curl -s --netrc https://api.example.com/data

Dateien herunterladen und hochladen

Einfacher Download — mit Originaldateinamen speichern:

curl -O https://releases.ubuntu.com/24.04/ubuntu-24.04-desktop-amd64.iso

Download in einen bestimmten Pfad:

curl -o /tmp/ubuntu.iso https://releases.ubuntu.com/24.04/ubuntu-24.04-desktop-amd64.iso

Weiterleitungen folgen (viele Download-URLs leiten weiter):

curl -L -O https://github.com/cli/cli/releases/latest/download/gh_linux_amd64.tar.gz

Einen unterbrochenen Download fortsetzen:

curl -C - -O https://example.com/large-file.tar.gz

Download mit Fortschrittsbalken (nützlich in interaktiven Terminals):

curl --progress-bar -O https://example.com/large-file.tar.gz

Eine Datei mit multipart/form-data hochladen (simuliert ein Browser-Datei-Eingabefeld):

curl -s -X POST \
  -F "file=@/path/to/document.pdf" \
  -F "description=My document" \
  https://api.example.com/upload | jq .

Rohe Binärdaten mit PUT hochladen:

curl -s -X PUT \
  -H "Content-Type: application/octet-stream" \
  --data-binary @/path/to/image.png \
  https://api.example.com/files/image.png

Ausführliches Debugging mit -v und —trace

Das Flag -v ist dein bestes Debugging-Werkzeug. Es gibt den TLS-Handshake, Anfrage-Header, Antwort-Header und die Status-Zeile aus:

curl -v https://jsonplaceholder.typicode.com/posts/1

Beispielausgabe (gekürzt):

* Connected to jsonplaceholder.typicode.com (104.21.x.x) port 443
* TLSv1.3, TLS handshake
> GET /posts/1 HTTP/2
> Host: jsonplaceholder.typicode.com
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/2 200
< content-type: application/json; charset=utf-8
< cache-control: max-age=43200
<
{ ... JSON body ... }

Mit > beginnende Zeilen werden von curl gesendet; < kommt vom Server. Das ist unschätzbar wertvoll, um zu überprüfen, ob deine Header korrekt gesendet werden.

Für noch mehr Details verwende --trace, um rohe Bytes zu dumpen:

curl --trace /tmp/curl-trace.txt https://api.example.com/endpoint

Nur Header prüfen ohne Body herunterzuladen:

curl -I https://example.com

-I sendet eine HEAD-Anfrage. Um HEAD zu senden und trotzdem eine benutzerdefinierte Methode anzugeben:

curl -s -X HEAD -I https://example.com

TLS-Zertifikatsinformationen prüfen:

curl -v --connect-to :: https://example.com 2>&1 | grep -A5 "Server certificate"

Oder mit --cert-status bei unterstützten Builds.

curl vs wget: Das richtige Werkzeug wählen

Sowohl curl als auch wget laden Dateien herunter, dienen jedoch unterschiedlichen Hauptzwecken:

Featurecurlwget
HauptanwendungsfallAPI-Aufrufe, HTTP-Debugging, SkriptingRekursive Downloads, Website-Mirroring
REST API-UnterstützungVollständig (GET/POST/PUT/DELETE/PATCH)Standardmäßig nur GET
JSON-Anfrage-Body-d '{"key":"val"}'Nicht nativ unterstützt
Benutzerdefinierte Header-H "Header: Value"--header="Header: Value"
Weiterleitungen folgen-L Flag erforderlichFolgt standardmäßig
Downloads fortsetzen-C --c Flag
Rekursiver DownloadNicht unterstützt--recursive Flag
Ausgabe auf stdoutStandardErfordert -O -
Skripting / PipingAusgezeichnet (stdout standardmäßig)Weniger natürlich
Unterstützte Protokolle30+ (FTP, SFTP, SMTP, IMAP…)HTTP, HTTPS, FTP
Fortschrittsanzeige-# oder --progress-barWird standardmäßig angezeigt

Faustregel: Verwende curl für API-Arbeit, Debugging und Skripting-Pipelines. Verwende wget, wenn du eine Website spiegeln oder rekursiv herunterladen musst, oder wenn du Downloads mit einfacherer Syntax fortsetzen möchtest.

Praxisszenario: Eine REST API von Anfang bis Ende testen

Du betreibst einen Produktionsserver mit einem neuen User-Management-Microservice. Vor dem Deployment möchtest du den vollständigen CRUD-Lebenszyklus von der Kommandozeile aus validieren und Statuscodes für die CI/CD-Integration erfassen.

#!/bin/bash
set -euo pipefail
BASE="https://api.example.com/v1"
TOKEN="eyJhbGciOiJIUzI1NiIs..."

echo "=== Testing User Management API ==="

# 1. Health check
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/health")
echo "Health check: $STATUS"
[[ "$STATUS" == "200" ]] || { echo "FAIL: API not healthy"; exit 1; }

# 2. Create a new user
RESPONSE=$(curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","email":"test@example.com","role":"viewer"}' \
  "$BASE/users")
USER_ID=$(echo "$RESPONSE" | jq -r '.id')
echo "Created user ID: $USER_ID"

# 3. Retrieve the user
curl -s -H "Authorization: Bearer $TOKEN" \
  "$BASE/users/$USER_ID" | jq '.username'

# 4. Update the user role
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
  -X PATCH \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"role":"editor"}' \
  "$BASE/users/$USER_ID")
echo "PATCH status: $STATUS"
[[ "$STATUS" == "200" ]] || echo "WARNING: expected 200, got $STATUS"

# 5. List all users and count
COUNT=$(curl -s -H "Authorization: Bearer $TOKEN" \
  "$BASE/users" | jq 'length')
echo "Total users: $COUNT"

# 6. Delete test user
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
  -X DELETE \
  -H "Authorization: Bearer $TOKEN" \
  "$BASE/users/$USER_ID")
echo "DELETE status: $STATUS"
[[ "$STATUS" == "204" ]] || echo "WARNING: expected 204, got $STATUS"

echo "=== All tests passed ==="

Dieses Skript validiert den gesamten API-Vertrag und beendet sich mit einem Nicht-Null-Code, wenn etwas fehlschlägt — bereit für den Einsatz in einer CI/CD-Pipeline.

Fallstricke und Sonderfälle

SSL-Zertifikatsfehler bei internen/Entwicklungsservern: Verwende -k oder --insecure, um die Überprüfung zu überspringen, aber nur in der Entwicklung:

curl -k https://internal-dev-server.local/api

Einfache vs. doppelte Anführungszeichen in Shells: Innerhalb doppelter Anführungszeichen werden $ und Backticks expandiert. Verwende einfache Anführungszeichen um JSON-Bodies, um Überraschungen zu vermeiden:

# Sicher: einfache Anführungszeichen verhindern Variablenexpansion im JSON
curl -d '{"userId":1}' https://api.example.com/items

# Riskant: $HOME wird innerhalb doppelter Anführungszeichen expandiert
curl -d "{\"userId\":$USER_ID}" https://api.example.com/items  # OK wenn $USER_ID beabsichtigt ist

Ein wörtliches @ in POST-Daten senden: curl behandelt -d @dateiname als “Body aus Datei lesen”. Um ein wörtliches @ zu senden, verwende --data-raw:

curl --data-raw '{"email":"user@example.com"}' https://api.example.com/subscribe

Rate Limiting und Wiederholungsversuche: Füge --retry und --retry-delay für robuste Skripte hinzu:

curl --retry 5 --retry-delay 2 --retry-all-errors \
  -s https://api.example.com/endpoint | jq .

Timeout-Kontrolle: Verhindere, dass Skripte ewig hängen:

curl --connect-timeout 10 --max-time 30 https://api.example.com/slow-endpoint

--connect-timeout begrenzt die Verbindungsphase; --max-time begrenzt die gesamte Übertragungszeit.

Cookies: Cookies für sitzungsbasierte APIs senden und speichern:

# Cookies nach dem Login in einer Datei speichern
curl -s -c /tmp/cookies.txt -X POST \
  -d "username=admin&password=secret" \
  https://example.com/login

# Gespeicherte Cookies für authentifizierte Anfragen wiederverwenden
curl -s -b /tmp/cookies.txt https://example.com/dashboard

Fehlerbehebung

“Could not resolve host”: DNS-Fehler. Prüfe /etc/resolv.conf oder teste mit curl --dns-servers 8.8.8.8 https://example.com.

“SSL: no alternative certificate subject name matches”: Das Serverzertifikat stimmt nicht mit dem Hostnamen überein. Verwende -v, um den Zertifikatsinhalt zu sehen; der Hostname in deiner URL ist möglicherweise falsch.

“Empty reply from server”: Der Server hat die Verbindung ohne gesendete Header geschlossen. Prüfe mit -v, ob der TLS-Handshake erfolgreich war; dies kann auf einen Protokoll-Mismatch hinweisen (--http1.1 Flag kann helfen).

Antwort-Body ist leer, aber Status ist 200: Einige Endpunkte geben bei Erfolg 204 No Content zurück (besonders DELETE). Verwende -v, um die Status-Zeile zu bestätigen.

Große Antwort im Terminal abgeschnitten: Leite an less weiter oder leide in eine Datei um:

curl -s https://api.example.com/large-dataset | jq . | less
curl -s https://api.example.com/large-dataset > /tmp/response.json

Prüfen, welche curl-Version du hast und welche Protokolle unterstützt werden:

curl --version

Suche in der Ausgabe nach Protocols: — dort siehst du FTP-, SFTP-, HTTP/2- und HTTP/3-Verfügbarkeit je nach deinem Build.

Zusammenfassung

  • curl ist der Standard-Linux-HTTP-Client für REST-API-Tests, Dateiübertragungen und Netzwerk-Debugging
  • Verwende -X zum Setzen der HTTP-Methode (POST, PUT, PATCH, DELETE), -H für Header, -d für den Anfrage-Body
  • Authentifizierung: -H "Authorization: Bearer TOKEN" für Token-Auth, -u benutzer:passwort für Basic Auth
  • Dateien herunterladen mit -O (Originalname) oder -o dateiname; -L zum Folgen von Weiterleitungen verwenden
  • -v Verbose-Modus zeigt vollständige Anfrage-/Antwort-Header und TLS-Details — unverzichtbar beim Debuggen
  • -w "%{http_code}" extrahiert den Statuscode für Skripting und CI/CD-Validierung
  • Verwende --retry, --connect-timeout und --max-time, um Skripte produktionstauglich zu machen
  • Bevorzuge curl gegenüber wget für API-Arbeit und Skripting; verwende wget für rekursive Website-Downloads
  • Verwende immer --data-raw, wenn Body-Daten wörtliche @-Zeichen enthalten, um Datei-Lese-Verwechslungen zu vermeiden
  • Leite an jq weiter für formatierte JSON-Ausgabe: curl -s https://api.example.com/data | jq .

Verwandte Artikel