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.
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).
GET /time HTTP/1.1 Host: localhost:8000 User-Agent: curl/8.5.0 Accept: */*
GET), der Pfad (/time) und die Version des Protokolls.\r\n, die leere also nur aus \r\n.POST folgt nach der leeren Zeile der Inhalt, zum Beispiel ausgefüllte Formularfelder oder JSON.HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Content-Length: 21 Connection: close Es ist 10:46:44 Uhr.
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.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.
| 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.
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. |
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. |
http oder https.http, 443 bei https.GET /hello?name=Ada%20Lovelace HTTP/1.1. Die Query bringt Parameter mit, name=wert, mehrere getrennt durch &.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(…).
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.
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.
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 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.
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.