Grundlagen

HTTP

Das Protokoll, das jeder benutzt und kaum jemand je roh gesehen hat: Anfrage und Antwort, Methoden und Statuscodes, Caching mit ETag – und warum HTTP/2 und HTTP/3 erfunden wurden.

Lesestand

HTTP ist ein einfaches Frage-Antwort-Protokoll: Der Client schickt eine Anfrage – eine Methode wie GET, einen Pfad, ein paar Kopfzeilen und eventuell einen Inhalt. Der Server schickt eine Antwort – einen dreistelligen Statuscode, Kopfzeilen und meist einen Inhalt. Bis HTTP/1.1 war das alles lesbarer Text.

HTTP ist zustandslos: Jede Anfrage steht für sich. Anmeldungen, Warenkörbe und Sitzungen entstehen erst durch Cookies oder Tokens. Die neueren Versionen ändern nicht die Bedeutung, sondern den Transport: HTTP/2 bündelt viele Anfragen auf einer Verbindung, HTTP/3 läuft über QUIC statt TCP.

Ein Gespräch in Textform

Unter der Haube von Browser, REST-Client und curl sieht ein Austausch in HTTP/1.1 so aus – genau so, Zeichen für Zeichen, über eine TCP-Verbindung:

# Anfrage GET /api/bestellungen/42 HTTP/1.1 Host: shop.example.com Accept: application/json User-Agent: Java-http-client/21 # Antwort HTTP/1.1 200 OK Content-Type: application/json Content-Length: 47 Cache-Control: max-age=60 {"id":42,"status":"versandt","summe":"59.90"}

Eine Zeile mit Methode, Pfad und Version, dann Kopfzeilen (Header) als Name: Wert, dann eine Leerzeile, dann der Inhalt (Body). Der Host-Header sagt, welche Website gemeint ist – so können Tausende Domains auf einem Server mit einer IP-Adresse wohnen. Content-Type sagt, was im Body steckt, Content-Length, wie lang er ist – denn TCP kennt keine Nachrichtengrenzen.

Selbst eine Anfrage bauen

Hier ist eine kleine Bestell-API. Stell die Anfrage zusammen und schau, was der Server antwortet:

Anfrage-Baukasten
Header

Probier zum Beispiel: GET auf 42, dann mit If-None-Match. POST ohne und mit Anmeldung. DELETE auf die ganze Sammlung.

Methoden: sicher und idempotent

Die Methode sagt, was der Client vorhat. Zwei Eigenschaften sind dabei wichtiger, als sie klingen:

  • Sicher heißt: Die Anfrage ändert nichts. Ein Crawler, ein Vorab-Laden im Browser oder ein Cache darf sie jederzeit ausführen.
  • Idempotent heißt: Zweimal ausgeführt hat dieselbe Wirkung wie einmal. Das ist entscheidend bei Netzproblemen: Kam keine Antwort, darf man eine idempotente Anfrage gefahrlos wiederholen. Ein doppeltes POST dagegen legt womöglich zwei Bestellungen an.
MethodeBedeutungsicheridempotent
GETlesen✓✓
HEADwie GET, aber nur die Header✓✓
POSTetwas Neues anlegen oder eine Aktion auslösen––
PUTeine Ressource vollständig ersetzen oder unter bekanntem Namen anlegen–✓
PATCHeine Ressource teilweise ändern–nicht garantiert
DELETElöschen–✓
OPTIONSfragen, was erlaubt ist – etwa für CORS-Prüfungen im Browser✓✓

Das sind Zusagen, keine Durchsetzung: Ein Server kann bei GET etwas löschen. Er sollte es nur nie tun – sonst räumt der nächste Suchmaschinen-Crawler die Datenbank leer.

Statuscodes

Die erste Ziffer sagt, wer gefragt ist: 2 – alles gut, 3 – woanders schauen, 4 – der Client hat etwas falsch gemacht, 5 – der Server hat ein Problem. Klick auf einen Code:

Die wichtigsten Statuscodes
Code anklicken.

Zustandslos: Cookies und Tokens

Für den Server ist jede Anfrage eine neue Begegnung. Damit er wiedererkennt, wer fragt, muss der Client es bei jeder Anfrage mitschicken. Zwei Wege:

🍪

Cookie

Der Server antwortet mit Set-Cookie: sitzung=a81f…, der Browser schickt es bei jeder weiteren Anfrage an diese Domain automatisch mit. Wichtige Zusätze: HttpOnly (kein Zugriff per JavaScript), Secure (nur über HTTPS), SameSite (nicht bei Anfragen von fremden Seiten).

🔑

Token

Der Client schickt bei jeder Anfrage Authorization: Bearer …. Typisch für APIs und Apps. Oft ein JWT – ein signierter Datensatz, den der Server ohne Datenbankabfrage prüfen kann.

⇄

Warum zustandslos gut ist

Jeder Server kann jede Anfrage beantworten. Ein Lastverteiler kann sie beliebig verteilen, ein ausgefallener Server ist sofort ersetzbar.

Caching

Das Schnellste ist eine Anfrage, die gar nicht erst gestellt wird. HTTP hat dafür zwei Mechanismen, beide im Baukasten oben zu sehen:

  • Frische: Cache-Control: max-age=60 erlaubt jedem Cache, die Antwort 60 Sekunden lang ohne Rückfrage wiederzuverwenden. Für Dateien, die sich nie ändern – etwa app.3f9c2a.js mit dem Inhalt im Namen –, setzt man ein Jahr.
  • Prüfung: Danach fragt der Client: „Ich habe Version "v7" – gibt es Neues?“ (If-None-Match). Ist nichts neu, antwortet der Server mit 304 Not Modified, ganz ohne Inhalt. Das ETag ist dabei ein Versionsstempel, den der Server frei wählt, etwa eine Prüfsumme.

HTTP/1.1, HTTP/2, HTTP/3

HTTP/1.1 von 1997 hat ein Problem: Auf einer Verbindung geht immer nur eine Anfrage zur Zeit. Browser öffnen deshalb bis zu sechs Verbindungen pro Server. HTTP/2 (2015) verpackt alles in binäre Rahmen und schickt beliebig viele Anfragen gleichzeitig über eine Verbindung. HTTP/3 (2022) macht dasselbe über QUIC. Eine Webseite mit zwölf kleinen Dateien:

Zwölf Dateien laden
Rundreise (RTT)
Verbindungsaufbau (TCP + TLS bzw. QUIC)Anfrage und AntwortWarten auf das verlorene Paket

Stark vereinfacht: Bandbreite unbegrenzt, Server antwortet sofort, Verlust kostet eine zusätzliche Rundreise.

Für Entwickler übersetzt

HttpClient client = HttpClient.newBuilder()
        .connectTimeout(Duration.ofSeconds(3))
        .version(HttpClient.Version.HTTP_2)        // fällt bei Bedarf auf 1.1 zurück
        .build();                                   // einmal bauen, wiederverwenden: hält Verbindungen offen

HttpRequest req = HttpRequest.newBuilder(URI.create("https://shop.example.com/api/bestellungen/42"))
        .header("Accept", "application/json")
        .timeout(Duration.ofSeconds(5))
        .GET()
        .build();

HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString());
if (res.statusCode() != 200) {                  // 404 oder 500 werfen KEINE Exception
    throw new IllegalStateException("HTTP " + res.statusCode());
}
System.out.println(res.version() + " " + res.headers().firstValue("etag").orElse("-"));
StolpersteinWas dahintersteckt
Ein Client pro AnfrageJeder neue HttpClient baut eigene Verbindungen auf – samt Handshakes. Einen Client für die ganze Anwendung halten.
Fehler-Status ohne ExceptionDer JDK-Client wirft nur bei Netzproblemen. Den Statuscode muss man selbst prüfen – anders als etwa bei Springs RestClient, der bei 4xx und 5xx Exceptions wirft.
Header-NamenGroß- und Kleinschreibung ist egal. In HTTP/2 und HTTP/3 werden sie immer kleingeschrieben übertragen.
WiederholenNur idempotente Anfragen automatisch wiederholen. Für POST hilft ein Idempotenz-Schlüssel im Header, den der Server sich merkt.
Roh anschauencurl -v https://… zeigt Anfrage und Antwort samt Headern – meist der schnellste Weg, ein API-Problem einzugrenzen.

Häufige Irrtümer

„POST ist sicherer als GET.“ – Ohne Verschlüsselung kann man beides mitlesen, mit HTTPS keins von beiden. Der echte Unterschied: GET-Parameter landen in Browserverlauf und Server-Logs. Passwörter gehören deshalb nie in die URL.
„Status 200 heißt: hat geklappt.“ – Manche APIs antworten auch bei Fehlern mit 200 und einer Fehlermeldung im Body. Guter Stil ist das nicht, aber man muss damit rechnen.
„PUT und POST sind austauschbar.“ – Der Unterschied zeigt sich bei Wiederholungen: PUT ist idempotent, POST nicht.
„HTTPS ist ein anderes Protokoll.“ – Es ist dasselbe HTTP, nur durch eine TLS-Verschlüsselung getunnelt. Mehr dazu im Eintrag zu TLS.
„HTTP/2 ist immer schneller.“ – Meist ja. Auf Verbindungen mit vielen Paketverlusten kann es aber schlechter sein als mehrere HTTP/1.1-Verbindungen, weil ein Verlust alle Anfragen auf der einen TCP-Verbindung aufhält.

Verwandte Begriffe