Geonode logo
Geonode Team

Geonode Team

Aktualisiert: 7. Oktober 2026

Veröffentlicht: 02.09.2026

So führen Sie eine HEAD-Anfrage mit curl durch

`curl -I` sendet eine HEAD-Anfrage. `curl -X HEAD` scheint dasselbe tun zu sollen, führt jedoch eine subtil fehlerhafte Aktion aus – eine hervorragende Möglichkeit, zwanzig Minuten mit einem hängenden Terminal zu verschwenden. Das „curl“-Handbuch weist ausdrücklich darauf hin, und es lohnt sich, den Grund dafür zu verstehen, da er für jede Methode gilt, die man manuell festlegen könnte. Dieser Leitfaden behandelt die korrekte Syntax, die Vorgaben der Spezifikation darüber, was Server weglassen dürfen, sowie die Fälle, in denen ein HEAD-Aufruf Informationen liefert, die ein GET-Aufruf nicht liefern würde.

Warum uns das wichtig ist: Wir sind Geonode und verkaufen Proxys, sodass Nutzer ständig HEAD-Anfragen über uns stellen, um Links, Dateigrößen und Verfügbarkeit kostengünstig zu überprüfen. Die ehrliche Warnung lautet: HEAD ist eine andere Anfrage als ein „leichtgewichtiger“ GET, und wenn man sie als solchen behandelt, kommt man zu falschen Schlussfolgerungen. Eine URL, die bei einem HEAD einen 200-Status zurückgibt, kann bei einem GET einen 403-Status zurückgeben. Eine Ressource, die bei einem HEAD-Aufruf keine „Content-Length“ anzeigt, kann bei einem GET-Aufruf eine solche enthalten. Und eine Anti-Bot-Schicht könnte einen ungewöhnlichen HEAD-Aufruf als eigenes Signal interpretieren. HEAD eignet sich hervorragend für seinen eigentlichen Zweck; als Ersatz für die Frage „Was würde passieren, wenn ich dies tatsächlich abrufen würde?“ ist es jedoch ungeeignet.

Die richtige Vorgehensweise bei „

curl -I https://example.com

“

Im curl-Handbuch wird „-I, --head

“ wie folgt beschrieben: „(HTTP FTP FILE) Ruft nur die Header ab. HTTP-Server verfügen über den Befehl HEAD, den diese Funktion nutzt, um ausschließlich den Header eines Dokuments abzurufen. Bei Verwendung mit einer FTP- oder FILE-URL zeigt curl lediglich die Dateigröße und den Zeitpunkt der letzten Änderung an.“

Ausgabe:

HTTP/2 200
content-type: text/html; charset=UTF-8
content-length: 1256
last-modified: Thu, 17 Oct 2019 07:18:26 GMT
cache-control: max-age=604800

Das ist die vollständige Antwort auf die Frage in der Überschrift. Was nun folgt, ist der Teil, der Zeit spart.

Warum „-X HEAD“ falsch ist

Das curl-Handbuch geht direkt unter „-X, --request“ darauf ein:

Diese Option ändert lediglich das tatsächlich in der HTTP-Anfrage verwendete Wort, sie verändert jedoch nicht das Verhalten von curl. Wenn Sie beispielsweise eine korrekte HEAD-Anfrage stellen möchten, reicht die Verwendung von „-X HEAD“ nicht aus. Sie müssen die Option „--head“ verwenden.

Der Mechanismus: „-X“ tauscht lediglich die Methodenzeile aus, sonst nichts. „curl“ verhält sich weiterhin so, als würde es einen GET-Aufruf ausführen, was bedeutet, dass es nach wie vor einen Antworttext erwartet. Der Server, der HEAD korrekt implementiert, sendet Header und keinen Body. curl wartet auf Inhalte, die niemals eintreffen werden, und der Befehl scheint zu hängen, bis ein Timeout oder das Schließen der Verbindung ihn beendet.

Das Handbuch warnt zudem vor einem zweiten Verhalten von „-X“, das Nutzer bei Weiterleitungen in die Irre führt: „Wenn ‚--location‘ verwendet wird, wird die mit ‚--request‘ festgelegte Methodenzeichenfolge für alle Anfragen verwendet.“ Somit sendet „-X POST -L“ bei jedem Schritt in einer Weiterleitungskette erneut einen POST-Befehl, was selten beabsichtigt ist.

Das allgemeine Prinzip, wie es im Handbuch selbst beschrieben wird: „Normalerweise benötigen Sie diese Option nicht. Alle Arten von GET-, HEAD-, POST- und PUT-Anfragen werden vielmehr über spezielle Befehlszeilenoptionen aufgerufen.“ Verwenden Sie -I für HEAD, -d für POST, -T für PUT und reservieren Sie -X für wirklich ungewöhnliche Methoden wie PROPFIND.

Was HEAD eigentlich ist

RFC 9110 §9.3.2 definiert es in einem Satz:

Die HEAD-Methode ist identisch mit GET, mit der Ausnahme, dass der Server in der Antwort KEINE Inhalte senden DARF.

Und es wird der Zweck wie folgt beschrieben: „HEAD wird verwendet, um Metadaten über die ausgewählte Repräsentation abzurufen, ohne deren Repräsentationsdaten zu übertragen, häufig zum Testen von Hypertext-Links oder zum Ermitteln aktueller Änderungen.“

Das ist eine strenge Anforderung an Server – MUST NOT Inhalte senden – und deshalb hängt sich curl auf, wenn es darauf vorbereitet ist, welche zu erhalten.

Die Regel für die Header ist bewusst schwächer formuliert, und genau hier liegt häufig ein Missverständnis:

Der Server SOLLTE als Antwort auf eine HEAD-Anfrage dieselben Header-Felder senden, die er auch gesendet hätte, wenn die Anfragemethode GET gewesen wäre. Ein Server DARF jedoch Header-Felder weglassen, deren Wert erst bei der Generierung des Inhalts ermittelt wird.

Der RFC nennt ein konkretes Beispiel: Server, die dynamische Antworten zwischenspeichern, können bei einem GET-Aufruf die Header „Content-Length“ und „Vary“ erzeugen, die „nicht innerhalb einer HEAD-Antwort generiert werden“. Er bezeichnet dies als „geringfügige Inkonsistenzen“ und hält sie für „besser als die Generierung und das Verwerfen des Inhalts für eine HEAD-Anfrage, da HEAD-Anfragen in der Regel aus Effizienzgründen gestellt werden“.

Ein fehlendes „Content-Length“ bei einem HEAD-Aufruf ist also nicht unbedingt ein Fehler und nicht unbedingt aussagekräftig. Es kann sich einfach um einen Server handeln, der sich weigert, etwas zu berechnen, das er nur durch das Rendern der Seite erfahren hätte.

Es gibt außerdem eine Regel bezüglich Request-Bodies, die man kennen sollte, wenn man Tools entwickelt. Der Inhalt einer HEAD-Anfrage „hat keine allgemein definierte Semantik, kann die Bedeutung oder das Ziel der Anfrage nicht verändern und könnte dazu führen, dass manche Implementierungen die Anfrage ablehnen und die Verbindung schließen, da sie das Potenzial für einen Request-Smuggling-Angriff birgt“. Der RFC besagt, dass ein Client „in einer HEAD-Anfrage KEINEN Inhalt generieren SOLLTE“, sofern keine spezifische vorherige Vereinbarung vorliegt. Senden Sie keinen Request-Body mit einer HEAD-Anfrage.

Wozu HEAD gut ist

Tatsächlich nützliche Anwendungsfälle, bei denen jeweils eine vollständige Übertragung gegen ein paar hundert Byte eingetauscht wird.

Prüfen, ob eine URL noch aktiv ist:

curl -sI -o /dev/null -w '%{response_code}\n' https://example.com

Die Größe einer Datei vor dem Herunterladen ermitteln:

curl -sI https://example.com/large.iso | grep -i content-length

Eine Weiterleitungskette verfolgen und melden:

curl -sIL -o /dev/null -w '%{num_redirects} hops -> %{url_effective}\n' https://example.com

Prüfen, ob ein Server Range-Anfragen unterstützt, wodurch festgestellt wird, ob ein unterbrochener Download fortgesetzt werden kann:

curl -sI https://example.com/file.zip | grep -i accept-ranges

Aktualität prüfen, ohne herunterzuladen:

curl -sI https://example.com/data.json | grep -iE 'last-modified|etag'

Massen-Linkprüfung – dies ist die klassische Anwendung, bei der sich die Bandbreiteneinsparung besonders deutlich bemerkbar macht:

while read -r url; do
  code=$(curl -sIL -o /dev/null -w '%{response_code}' --max-time 10 "$url")
  echo "$code $url"
done < urls.txt

Bei begrenzter Bandbreite macht sich die Einsparung wirklich bemerkbar: Eine Linkprüfung, die normalerweise 500 KB pro URL übertragen würde, überträgt stattdessen nur wenige hundert Byte. Bei zehntausend URLs ist das der Unterschied zwischen fünf Gigabyte und wenigen Megabyte.

Wenn HEAD Sie in die Irre führt

Die Fehlerfälle, die der Grund für die Warnung am Anfang sind.

Der Server lehnt HEAD vollständig ab. 405 Method Not Allowed bei einer URL, bei der GET einwandfrei funktioniert. Selten bei statischen Inhalten, nicht ungewöhnlich bei APIs und Anwendungsendpunkten.

Der Server behandelt HEAD anders. Unterschiedliche Statuscodes, unterschiedliche Header, manchmal sogar ein völlig anderer Codepfad in der Anwendung. Der RFC erlaubt das Weglassen von inhaltsabhängigen Headern, und die Implementierungen variieren darin, inwieweit sie dies umsetzen.

Caches und CDNs können HEAD-Anfragen separat indizieren. Cache-Header bei einem HEAD-Aufruf können einen anderen Cache-Eintrag widerspiegeln als bei einem GET-Aufruf; daher ist ein HEAD-Aufruf kein zuverlässiges Mittel, um das Caching-Verhalten zu überprüfen.

Anti-Bot-Systeme reagieren unterschiedlich. Ein HEAD-Request von einem unbekannten Client ist an sich schon ein Signal, und die Antwort, die Sie erhalten, ist möglicherweise nicht dieselbe, die ein browserähnlicher GET-Request erhalten würde.

Die ``Content-Length -Angabe fehlt möglicherweise oder ist falsch. Dies ist gemäß der Spezifikation zulässig, bei dynamischen Inhalten üblich und eine schlechte Grundlage für eine Schätzung der Download-Größe bei generierten Inhalten.

Weiterleitungsketten können unterschiedlich ausfallen. Manche Server leiten GET- und HEAD-Anfragen an unterschiedliche Ziele weiter, insbesondere wenn Content-Negotiation im Spiel ist.

Wenn Sie wissen müssen, was eine echte Anfrage bewirken würde, stellen Sie eine echte Anfrage und verwerfen Sie den Body:

curl -sS -o /dev/null -D - https://example.com

Ein echter GET-Aufruf, Header auf stdout, Body verworfen. Sie zahlen die Bandbreite und erhalten eine genaue Antwort. Entscheiden Sie sich bewusst zwischen den beiden: -I , wenn es günstig sein soll, -o /dev/null -D - , wenn es genau sein soll.

Für große Ressourcen gibt es eine Zwischenlösung – fordern Sie ein Byte statt des gesamten Inhalts an:

curl -sS -r 0-0 -o /dev/null -D - https://example.com/large.iso

-r, --range ruft „einen Byte-Bereich (d. h. ein Teildokument)“ ab, daher holt 0-0 nur das erste Byte. Dies ist ein echter GET-Aufruf mit echtem GET-Verhalten, fast ohne Bandbreitenkosten. Der Hinweis aus dem Handbuch: „Bei vielen HTTP/1.1-Servern ist diese Funktion nicht aktiviert“, prüfen Sie daher zunächst, ob Accept-Ranges: bytes vorhanden ist, und rechnen Sie mit einer vollständigen Antwort, falls dies nicht der Fall ist.

Muster, die es wert sind, in Skripte übernommen zu werden

Die oben genannten Befehle werden deutlich nützlicher, wenn man ihnen ein wenig Struktur verleiht.

Ein Link-Checker, der ehrlich berichtet. Die naive Version behandelt jeden Status außer 200 als defekten Link, was zu Fehlalarmen bei Weiterleitungen und auf Servern führt, die HEAD-Anfragen ablehnen. Diese Version unterscheidet zwischen ihnen:

check() {
  local url="$1" code
  code=$(curl -sIL -o /dev/null --max-time 10 -w '%{response_code}' "$url")
  case "$code" in
    200) echo "OK       $url" ;;
    405) code=$(curl -sSL -o /dev/null --max-time 10 -w '%{response_code}' "$url")
         echo "GET:$code $url" ;;
    000) echo "TIMEOUT  $url" ;;
    *)   echo "$code     $url" ;;
  esac
}

Der „405“-Zweig ist entscheidend: Ein Server, der HEAD ablehnt, ist kein defekter Link, und nur durch einen erneuten Versuch mit GET lässt sich dies feststellen. 000 ist die Art und Weise, wie curl meldet, dass überhaupt keine HTTP-Antwort eingegangen ist, wodurch ein Netzwerkfehler von einem Serverfehler unterschieden wird.

Parallelität – mit Bedacht. Die Linkprüfung lässt sich auf peinliche Weise parallelisieren, und die Versuchung ist groß, sie in großem Umfang auszuführen. Widerstehen Sie dieser Versuchung:

xargs -P 8 -I{} sh -c 'check "$1"' _ {} < urls.txt

Acht ist ein vernünftiger Standardwert. Die Obergrenze liegt hier eher in der Rücksichtnahme gegenüber dem Server eines anderen als in Ihrer eigenen Kapazität, und ein Linkprüfer, der einen Vorfall zur Ratenbegrenzung auslöst, hat mehr gekostet, als er eingespart hat.

Legen Sie immer ein Timeout fest. Ein HEAD-Request an einen nicht reagierenden Host hängt genau so lange, wie es ein GET-Request tun würde. --max-time 10 mit --connect-timeout 5 begrenzt dies, und in einer Schleife über Tausende von URLs sorgt diese Begrenzung dafür, dass der Vorgang abgeschlossen wird.

Erfassen Sie die tatsächliche URL, nicht nur den Status. %{url_effective} nach -L zeigt Ihnen, wo ein Link tatsächlich gelandet ist, was aus „Dieser Link funktioniert“ ein „Dieser Link funktioniert und verweist nun auf eine andere Seite“ macht – was in der Regel die interessantere Erkenntnis ist.

Speichern Sie Ihre Ergebnisse im Cache. Das erneute Überprüfen jeder URL bei jedem Durchlauf verschwendet Bandbreite und Goodwill. Speichern Sie den Status sowie die ETag oder Last-Modified und verwenden Sie bei nachfolgenden Durchläufen bedingte Anfragen, sodass unveränderte Ressourcen nur eine 304 statt einer vollständigen Überprüfung erfordern.

HEAD über einen Proxy

Drei Dinge ändern sich, die alle wissenswert sind.

curl -I -x http://user:pass@proxy.example.com:9000 https://example.com

Bei HTTPS erfolgt der „CONNECT“ zuerst. curl baut vor dem HEAD-Aufruf einen Tunnel auf, und in der ausführlichen Ausgabe erscheint die Antwort des Proxys darauf vor der des Zielservers. --suppress-connect-headers blendet dies aus; %{http_connect} meldet den Status des Proxys getrennt vom Status des Zielservers.

Die Bandbreiteneinsparung ist der entscheidende Punkt. Bei datenvolumengestützter Abrechnung kostet ein HEAD-Request nur wenige hundert Bytes im Vergleich zu einer ganzen Seite. Für die Linkvalidierung, Verfügbarkeitsüberwachung und Größenprüfungen in großem Umfang ist dies der Unterschied zwischen einem erschwinglichen und einem teuren Auftrag. Es ist eine der wenigen wirklich umfangreichen Optimierungen, die bei einer Abrechnung pro Gigabyte möglich sind.

Blöcke und Herausforderungen verhalten sich jedoch unterschiedlich. Eine Anti-Bot-Schicht, die bei einem GET-Aufruf eine Herausforderungsseite ausliefert, könnte einen HEAD-Aufruf einfach ablehnen – oder umgekehrt. Wenn Sie HEAD verwenden, um zu prüfen, ob ein Ziel erreichbar ist, verifizieren Sie das Ergebnis mit einem echten GET-Aufruf an einer Stichprobe, bevor Sie es auf eine ganze Liste anwenden. Dies ist das „Silent-Failure“-Muster, über das wir in Warum das Testen von Proxys wichtig ist geschrieben haben: Die Anfrage ist erfolgreich, die Antwort ist falsch, und nichts weist Sie darauf hin.

Ein wissenswertes Detail zu curl: „-G, --get“ lässt sich mit „--head“ kombinieren. Im Handbuch wird darauf hingewiesen, dass bei Verwendung von „-G“ zusammen mit „--head“ „die POST-Daten stattdessen bei einer HEAD-Anfrage an die URL angehängt werden“ – nützlich, wenn du bei einem HEAD-Aufruf Abfrageparameter benötigst, die aus Schlüssel-Wert-Paaren aufgebaut sind.

Die Antwort richtig interpretieren

Das Beste aus den Rückmeldungen herausholen.

Zuerst der Status. „200 “ existiert. „301 “ / „302 “ wurde verschoben – füge „-L “ hinzu, um weiterzukommen. „403 “ wurde abgelehnt. „404 “ ist nicht mehr vorhanden. „405 “ bedeutet, dass der Server HEAD nicht akzeptiert; versuche es daher erneut mit einem GET. „429 “ bedeutet, dass du langsamer vorgehen solltest.

**Content-Length **, falls vorhanden; beachten Sie, dass die Spezifikation das Weglassen zulässt.

**Accept-Ranges: bytes ** bedeutet, dass wiederaufnehmbare Downloads und Bereichsanfragen verfügbar sind.

**Last-Modified und ETag ** ermöglichen bedingte Anfragen. -z sendet If-Modified-Since – im Handbuch wird dies als Anfrage nach „einer Datei, die nach dem angegebenen Zeitpunkt geändert wurde“ beschrieben – und --etag-compare übernimmt die ETag -Seite. Ein „304 Not Modified “ kostet fast nichts und ist die richtige Methode, um eine Ressource wiederholt abzufragen.

**Content-Type ** zeigt dir, was du erhalten hättest. text/html – wo du eigentlich JSON erwartet hättest – bedeutet in der Regel einen Fehler oder eine Anmeldeseite.

Für die maschinelle Verarbeitung sollten Sie die Textanalyse komplett überspringen:

curl -sI -o /dev/null -w '%{header_json}' https://example.com | jq

%{header_json} gibt alle Antwort-Header als JSON mit Namen in Kleinbuchstaben und Array-Werten aus, wodurch wiederholte Header korrekt verarbeitet werden und kein Parser mehr erforderlich ist. Wir haben dies und die anderen Überprüfungsoptionen unter Anzeige von Antwort-Headern mit curl behandelt.

Häufig gestellte Fragen

Wie sende ich eine HEAD-Anfrage mit curl?

curl -I https://example.com. Die vollständige Form lautet --head. Verwenden Sie nicht -X HEAD – im Handbuch wird ausdrücklich darauf hingewiesen, dass dies für eine korrekte HEAD-Anfrage „nicht ausreicht“, da dabei lediglich die Methodenstringe geändert wird, während curl weiterhin einen Antworttext erwartet.

Warum hängt sich curl -X HEAD auf?

Weil „-X“ nur das Wort in der Anforderungszeile ändert, nicht aber das Verhalten von curl. curl wartet weiterhin auf einen Antworttext, während der Server korrekterweise keinen sendet, da RFC 9110 vorschreibt, dass ein Server in einer HEAD-Antwort „KEINEN Inhalt senden DARF“. Verwenden Sie stattdessen „-I“.

Was ist der Unterschied zwischen HEAD und GET?

HEAD ist identisch mit GET, außer dass der Server den Hauptteil nicht senden darf. Es dient dazu, Metadaten abzurufen, ohne Inhalte zu übertragen, typischerweise zur Überprüfung von Links oder zur Aktualitätsprüfung. Server sollten dieselben Header senden wie bei GET, dürfen jedoch diejenigen weglassen, die erst bei der Generierung des Inhalts berechnet werden.

Gibt HEAD immer dieselben Header zurück wie GET?

Nein. Die Spezifikation besagt, dass Server dieselben Header senden SOLLTEN, jedoch diejenigen weglassen KÖNNEN, „für die ein Wert erst bei der Generierung des Inhalts ermittelt wird“ – als Beispiele werden Content-Length und Vary genannt. Diese geringfügigen Inkonsistenzen werden als vorteilhafter angesehen als das Generieren und Verwerfen eines Bodys.

Wie erhalte ich die Dateigröße, ohne die Datei herunterzuladen?

Mit „curl -sI URL | grep -i content-length“. Beachten Sie, dass der Header bei dynamisch generierten Inhalten fehlen kann, was die Spezifikation zulässt. Für eine zuverlässigere Antwort bei nahezu keinem Aufwand fordern Sie ein einzelnes Byte mit „-r 0-0“ an und lesen Sie den Header „Content-Range“.

Warum funktioniert eine URL im Browser, gibt aber bei curl -I den Status 405 zurück?

Weil der Server an diesem Endpunkt keine HEAD-Anfragen akzeptiert. 405 Method Not Allowed ist eine gültige Antwort auf eine HEAD-Anfrage von einem Server, der GET-Anfragen problemlos verarbeitet. Versuchen Sie es erneut als GET-Anfrage ohne Body: curl -sS -o /dev/null -D - URL.

Kann ich eine HEAD-Anfrage mit einem Body senden?

Das solltest du nicht tun. RFC 9110 besagt, dass Inhalte in einer HEAD-Anfrage „keine allgemein definierte Semantik haben“, die Bedeutung der Anfrage nicht verändern können und „dazu führen könnten, dass einige Implementierungen die Anfrage aufgrund ihres Potenzials als Request-Smuggling-Angriff ablehnen und die Verbindung schließen“. Clients SOLLTEN in einer HEAD-Anfrage KEINE Inhalte generieren.

Ist HEAD nützlich, um zu prüfen, ob ein Proxy funktioniert?

Teilweise. Es bestätigt die Konnektivität und gibt den Statuscode mit geringem Aufwand zurück, was einen guten Smoke-Test darstellt. Es gibt jedoch keinen Aufschluss darüber, ob ein echter GET-Aufruf erfolgreich wäre, da Anti-Bot-Schichten die beiden häufig unterschiedlich behandeln. Führen Sie zunächst mit echten GET-Aufrufen an einer Stichprobe eine Validierung durch, bevor Sie den HEAD-Ergebnissen einer gesamten Liste vertrauen.

Fazit

Zwei Befehle decken dies vollständig ab. „curl -I URL“ für eine korrekte HEAD-Anfrage und „curl -sS -o /dev/null -D - URL“, wenn Sie die Header erhalten möchten, die ein echter GET-Aufruf erzeugen würde. Was nicht funktioniert, ist „-X HEAD“, und das Handbuch erklärt dies deutlich: Es ändert zwar das Wort, nicht aber das Verhalten, sodass curl auf einen Body wartet, den der Server gar nicht senden darf.

Die Entscheidung hängt davon ab, welche der beiden Optionen Sie bevorzugen. HEAD ist deutlich ressourcenschonender – einige hundert Byte im Vergleich zu einer ganzen Seite –, was es zum richtigen Werkzeug für die Linkprüfung, Verfügbarkeitsüberwachung und Größenschätzung bei jedem Datenvolumen macht, insbesondere bei begrenzter Bandbreite. Es ist jedoch das falsche Werkzeug, um vorherzusagen, was ein echter Abruf zurückgeben würde, da Server berechtigt sind, inhaltsbezogene Header wegzulassen, HEAD-Anfragen möglicherweise gänzlich ablehnen und sie häufig über eine andere Logik weiterleiten.

Und wenn Sie die Genauigkeit eines GET-Aufrufs ohne den Bandbreitenverbrauch benötigen, ist „-r 0-0“ der zu selten genutzte Mittelweg: ein echter GET-Aufruf, der ein Byte abruft. Prüfen Sie zunächst „Accept-Ranges: bytes“, da viele Server Ihnen ohnehin die gesamte Datei ausliefern werden.