kind: reference

HTTP

Wie Browser und Webserver miteinander reden: Anfrage und Antwort, Methoden, Statuscodes, Header und Adressen, wie man das alles mit curl und im Browser ansieht, wie Daten zum Server kommen, und was HTTPS und WebSockets daran ändern. Die Beispiele stammen aus Ein eigener Webserver.

1. Anfrage und Antwort

HTTP ist ein Protokoll aus Text. Der Browser (oder curl, oder ein Programm) öffnet eine TCP-Verbindung zum Server, meist auf Port 80, schickt eine Anfrage (Request) und bekommt eine Antwort (Response). Beide haben dieselbe Form: eine erste Zeile, Header, eine leere Zeile, dann vielleicht ein Inhalt (Body).

Die Anfrage

GET /time HTTP/1.1
Host: localhost:8000
User-Agent: curl/8.5.0
Accept: */*

  • Die erste Zeile ist die Request-Line: die Methode (GET), der Pfad (/time) und die Version des Protokolls.
  • Danach folgen Header, einer pro Zeile, je ein Name, ein Doppelpunkt und ein Wert.
  • Eine leere Zeile beendet die Header. Jede Zeile endet mit \r\n, die leere also nur aus \r\n.
  • Bei POST folgt nach der leeren Zeile der Inhalt, zum Beispiel ausgefüllte Formularfelder oder JSON.

Die Antwort

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 21
Connection: close

Es ist 10:46:44 Uhr.
  • Die erste Zeile ist die Status-Line: die Version, ein Statuscode (200) und ein kurzer Text dazu (OK).
  • Content-Type sagt, was der Inhalt ist, damit der Browser weiß, ob er HTML zeigen, CSS anwenden oder ein Bild anzeigen soll.
  • Content-Length ist die Länge des Inhalts in Bytes, nicht in Zeichen: Ein ü sind in UTF-8 zwei Bytes. Stimmt die Zahl nicht, wartet der Browser auf mehr oder schneidet ab.
  • Nach der leeren Zeile kommt der Inhalt, hier ein Text, sonst eine Seite, ein Bild, JSON.

Eine Webseite braucht meist viele Anfragen: eine für die Seite und je eine für jede CSS-Datei, jedes Skript und jedes Bild, das sie einbindet.

2. Methoden und Statuscodes

Methoden

Methode Wofür
GET etwas holen: eine Seite, ein Bild, Daten. Ändert nichts auf dem Server; Werte stehen höchstens in der Adresse (?name=Ada).
POST Daten schicken, die etwas bewirken: ein Formular, eine neue Nachricht, eine Anmeldung. Die Daten stehen im Inhalt der Anfrage.
PUT · PATCH etwas ersetzen · einen Teil davon ändern
DELETE etwas löschen
HEAD wie GET, aber nur die Header der Antwort, ohne Inhalt; curl -I fragt so

Ein Browser schickt GET, wenn du eine Adresse eingibst oder einem Link folgst, und POST, wenn ein Formular mit method='post' abgeschickt wird. Die übrigen Methoden benutzen Programme, zum Beispiel ein Skript mit fetch.

Statuscodes

Die erste Ziffer sagt, wie es ausgegangen ist: 2 geklappt, 3 steht woanders, 4 die Anfrage war falsch, 5 der Server hatte ein Problem.

Code Bedeutung
101 Switching Protocols Der Server wechselt das Protokoll, etwa zu WebSocket.
200 OK Alles in Ordnung, hier ist der Inhalt.
201 Created · 204 No Content etwas angelegt · erledigt, ohne Inhalt in der Antwort
301 Moved Permanently · 302 Found Die Seite steht unter einer anderen Adresse, für immer · vorübergehend. Die neue Adresse steht im Header Location; der Browser folgt ihr von selbst.
303 See Other Die Antwort auf ein POST: »Sieh unter Location nach.« Der Browser holt diese Adresse mit GET, und Neuladen schickt das Formular nicht noch einmal.
304 Not Modified Die Kopie im Browser ist noch aktuell, nichts Neues zu schicken.
400 Bad Request Die Anfrage ergibt keinen Sinn, etwa kaputtes JSON.
401 Unauthorized · 403 Forbidden erst anmelden · angemeldet, aber nicht erlaubt
404 Not Found Unter diesem Pfad gibt es nichts.
405 Method Not Allowed Den Pfad gibt es, aber nicht mit dieser Methode.
500 Internal Server Error Das Programm auf dem Server ist abgestürzt; die Ursache steht im Log des Servers, nicht im Browser.
502 Bad Gateway · 503 Service Unavailable Ein Server davor erreicht das eigentliche Programm nicht · der Server ist gerade überlastet oder in Wartung.

Häufige Header

Header sind unabhängig von Groß- und Kleinschreibung: content-type ist dasselbe wie Content-Type.

Header Bedeutung
Host: example.org Anfrage: An welchen Namen geht die Anfrage? Ein Server kann viele Websites unter einer IP-Adresse haben.
User-Agent: … Anfrage: welches Programm fragt, etwa ein Browser oder curl
Accept: text/html Anfrage: was das Programm gern zurückbekäme
Content-Type: text/html; charset=utf-8 Was im Inhalt steht: text/html, text/css, text/javascript, application/json, image/png …; bei Text dazu die Kodierung
Content-Length: 196 Länge des Inhalts in Bytes
Location: /neu Antwort: wohin der Browser bei 301, 302, 303 oder 201 gehen soll
Set-Cookie: sid=… · Cookie: sid=… Die Antwort legt einen Cookie im Browser ab · der Browser schickt ihn bei jeder weiteren Anfrage an denselben Server mit. So erkennt ein Server, wer angemeldet ist.
Cache-Control: no-cache wie lange der Browser eine Kopie aufheben darf
Connection: close Nach dieser Antwort schließt der Server die Verbindung. Ohne den Header bleibt sie für weitere Anfragen offen.

4. Adressen

Teile einer Adresse

  • Schema: das Protokoll, http oder https.
  • Host: welcher Rechner gefragt wird, ein Name oder eine IP-Adresse.
  • Port: ohne Angabe 80 bei http, 443 bei https.
  • Pfad und Query gehen in die Request-Line: GET /hello?name=Ada%20Lovelace HTTP/1.1. Die Query bringt Parameter mit, name=wert, mehrere getrennt durch &.
  • Fragment: eine Stelle in der Seite. Es bleibt im Browser; der Server sieht es nie.

Leerzeichen, Umlaute und Zeichen wie & oder ? haben in einer Adresse eine Bedeutung oder sind nicht erlaubt. Sie werden kodiert: % und der Wert des Bytes als Hexzahl, ein Leerzeichen ist %20, ein ü sind die zwei Bytes %C3%BC. In Ruby macht URI.decode_www_form daraus wieder Text, in JavaScript new URLSearchParams(…).

5. Ansehen und ausprobieren

curl

curl schickt eine Anfrage aus dem Terminal und zeigt die Antwort:

curl localhost:8000/time           # nur der Inhalt
curl -i localhost:8000/time        # mit Status-Line und Headern
curl -I localhost:8000/style.css   # nur die Header (Methode HEAD)
curl -v localhost:8000/time        # auch die Anfrage, mit > markiert
curl -L http://example.org/alt     # Weiterleitungen (301, 302) folgen

Daten mit POST schicken:

curl -d "name=Ada&klasse=10b" localhost:8000/anmelden            # wie ein Formular
curl --json '{"x": 3, "y": 2, "color": 5}' localhost:8000/pixel  # als JSON
curl -H "Accept: application/json" localhost:8000/api/time       # ein eigener Header

Liefert ein Server JSON, macht jq es lesbar: curl -s https://api.github.com/users/octocat | jq. Mehr dazu im Handbuch JSON.

Im Browser: der Reiter »Network«

F12 öffnet die Entwicklerwerkzeuge des Browsers, der Reiter Network (Netzwerk) zeigt jede Anfrage, die eine Seite auslöst, mit Methode, Statuscode, Typ, Größe und Dauer. Ein Klick auf eine Zeile zeigt die Header von Anfrage und Antwort und den Inhalt. Lade die Seite neu, während der Reiter offen ist; sonst ist die Liste leer.

So findest du zum Beispiel ein Bild, das nicht lädt: Es steht rot mit 404 in der Liste, und der Pfad daneben zeigt, wo der Browser gesucht hat.

6. Daten schicken

Formulare und JSON

Ein Formular schickt seine Felder als name=wert-Paare. Mit method='get' hängen sie an der Adresse (/suche?q=Pendel), mit method='post' stehen sie im Inhalt der Anfrage:

POST /anmelden HTTP/1.1
Host: localhost:8000
Content-Type: application/x-www-form-urlencoded
Content-Length: 19

name=Ada&klasse=10b

Programme schicken Daten oft als JSON. Dann sagt der Header Content-Type: application/json, was im Inhalt steht. In JavaScript:

const response = await fetch('/pixel', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ x: 3, y: 2, color: 5 }),
});
const data = await response.json();

Ein Server liest den Inhalt genau so lang, wie Content-Length sagt. Er darf keiner Anfrage trauen: Jeder kann mit curl beliebige Daten an jeden Pfad schicken, nicht nur das eigene Formular.

HTTPS

HTTPS ist dasselbe HTTP, aber in einer verschlüsselten Verbindung (TLS), meist auf Port 443. Wer unterwegs mitliest, sieht nur, mit welchem Server gesprochen wird, nicht den Pfad, die Header oder den Inhalt. Das Zertifikat des Servers beweist, dass er wirklich zu dem Namen gehört; der Browser zeigt sonst eine Warnung.

Im Workspace sprechen deine eigenen Server einfaches HTTP. Die weitergeleiteten Adressen aus dem Reiter »Ports« und die Shared Live Apps bekommen HTTPS vom Workspace davor.

WebSockets

HTTP ist Frage und Antwort: Der Server kann nichts schicken, worum niemand gefragt hat. Ein WebSocket beginnt als HTTP-Anfrage mit Upgrade: websocket und Sec-WebSocket-Key; der Server antwortet mit 101 Switching Protocols und Sec-WebSocket-Accept. Danach bleibt die Verbindung offen, und beide Seiten schicken sich Nachrichten in kleinen Frames, wann sie wollen. In JavaScript:

const socket = new WebSocket('wss://' + location.host + '/ws');
socket.addEventListener('message', (event) => console.log(event.data));
socket.send('Hallo');

Wie der Handshake im Einzelnen geht, zeigt Eine Pixelwand für alle.