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.
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:
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:
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
POSTdagegen legt womöglich zwei Bestellungen an.
| Methode | Bedeutung | sicher | idempotent |
|---|---|---|---|
GET | lesen | ✓ | ✓ |
HEAD | wie GET, aber nur die Header | ✓ | ✓ |
POST | etwas Neues anlegen oder eine Aktion auslösen | – | – |
PUT | eine Ressource vollständig ersetzen oder unter bekanntem Namen anlegen | – | ✓ |
PATCH | eine Ressource teilweise ändern | – | nicht garantiert |
DELETE | löschen | – | ✓ |
OPTIONS | fragen, 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:
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=60erlaubt jedem Cache, die Antwort 60 Sekunden lang ohne Rückfrage wiederzuverwenden. Für Dateien, die sich nie ändern – etwaapp.3f9c2a.jsmit 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 mit304 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:
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("-"));| Stolperstein | Was dahintersteckt |
|---|---|
| Ein Client pro Anfrage | Jeder neue HttpClient baut eigene Verbindungen auf – samt Handshakes. Einen Client für die ganze Anwendung halten. |
| Fehler-Status ohne Exception | Der 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-Namen | Groß- und Kleinschreibung ist egal. In HTTP/2 und HTTP/3 werden sie immer kleingeschrieben übertragen. |
| Wiederholen | Nur idempotente Anfragen automatisch wiederholen. Für POST hilft ein Idempotenz-Schlüssel im Header, den der Server sich merkt. |
| Roh anschauen | curl -v https://… zeigt Anfrage und Antwort samt Headern – meist der schnellste Weg, ein API-Problem einzugrenzen. |