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 --versionzur Überprüfung; Installation viasudo apt install curlodersudo 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:
jqinstalliert 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:
| Feature | curl | wget |
|---|---|---|
| Hauptanwendungsfall | API-Aufrufe, HTTP-Debugging, Skripting | Rekursive Downloads, Website-Mirroring |
| REST API-Unterstützung | Vollstä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 erforderlich | Folgt standardmäßig |
| Downloads fortsetzen | -C - | -c Flag |
| Rekursiver Download | Nicht unterstützt | --recursive Flag |
| Ausgabe auf stdout | Standard | Erfordert -O - |
| Skripting / Piping | Ausgezeichnet (stdout standardmäßig) | Weniger natürlich |
| Unterstützte Protokolle | 30+ (FTP, SFTP, SMTP, IMAP…) | HTTP, HTTPS, FTP |
| Fortschrittsanzeige | -# oder --progress-bar | Wird 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
curlist der Standard-Linux-HTTP-Client für REST-API-Tests, Dateiübertragungen und Netzwerk-Debugging- Verwende
-Xzum Setzen der HTTP-Methode (POST, PUT, PATCH, DELETE),-Hfür Header,-dfür den Anfrage-Body - Authentifizierung:
-H "Authorization: Bearer TOKEN"für Token-Auth,-u benutzer:passwortfür Basic Auth - Dateien herunterladen mit
-O(Originalname) oder-o dateiname;-Lzum Folgen von Weiterleitungen verwenden -vVerbose-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-timeoutund--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
jqweiter für formatierte JSON-Ausgabe:curl -s https://api.example.com/data | jq .