Hoppa till innehåll

HTTP

HTTP (Hypertext Transfer Protocol) är språket som webbläsare och webbservrar talar med varandra. Allt du bygger i den här kursen tar emot HTTP-förfrågningar och skickar HTTP-svar, så det här är veckans viktigaste sida. Den innehåller:

  • samspelet mellan klient och server
  • en URL:s delar
  • hur förfrågningar och svar ser ut
  • metoder, statuskoder och headers
  • hur data skickas till servern
  • cookies, och hur en server kan komma ihåg en användare
  • HTTPS och certifikat
  • verktygen för att se vad som skickas
  • övningar att göra på egen hand

Läs sidan Nätverk först. HTTP är det sista av de fyra stegen från URL till svar som beskrivs där.

Tidsåtgång: Ungefär 25–30 minuter att läsa och en och en halv timme för övningarna.

I HTTP är det alltid klienten som börjar. Den skickar en förfrågan (request), och servern svarar med ett svar (response). Klienten är oftast en webbläsare, men kan lika gärna vara curl, en mobilapp eller din egen Node-app som anropar ett API.

HTTP är tillståndslöst (stateless): varje förfrågan är fristående, och servern minns ingenting från den förra. Det gör att en server kan hantera väldigt många klienter, och att förfrågningar kan fördelas mellan flera servrar. Men det gör också att inloggning inte fungerar av sig själv. Hur det ändå löses tar avsnittet om cookies upp.

https://coursepress.lnu.se:443/kurser/sok?q=http&sida=2#resultat
└─┬─┘ └───────┬────────┘└─┬┘└────┬────┘└─────┬──────┘└───┬───┘
schema värd port sökväg querysträng fragment
Del Betydelse
Schema Protokollet, http eller https
Värd Domännamnet eller IP-adressen till servern
Port Utelämnas oftast. Standard är 80 för http och 443 för https.
Sökväg Vilken resurs på servern som efterfrågas
Querysträng Parametrar som namn=värde, åtskilda med &
Fragment En plats på sidan. Skickas aldrig till servern, utan används bara av webbläsaren.

HTTP/1.1 är text. En förfrågan består av en förfrågningsrad, headers, en tom rad och ibland en body:

POST /login HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 ...
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
username=ada&password=hemligt

Svaret har samma form, men börjar med en statusrad:

HTTP/1.1 302 Found
Location: /dashboard
Set-Cookie: session=4f2a9c...; HttpOnly; Secure; SameSite=Lax
Content-Length: 0

HTTP/2 och HTTP/3 skickar samma information, men binärt och effektivare. Metoder, statuskoder och headers betyder samma sak i alla versioner.

Metoden anger vad klienten vill göra med resursen.

Metod Används till Säker Idempotent
GET Hämta en resurs Ja Ja
HEAD Som GET, men bara headers Ja Ja
POST Skicka data, till exempel skapa något eller logga in Nej Nej
PUT Ersätta en resurs helt Nej Ja
PATCH Ändra delar av en resurs Nej Nej
DELETE Ta bort en resurs Nej Ja
OPTIONS Fråga vilka metoder som är tillåtna, används av webbläsaren för CORS Ja Ja

Säker betyder att metoden inte ändrar något på servern. En GET får därför aldrig ta bort eller ändra data. Idempotent betyder att samma förfrågan gjord flera gånger ger samma resultat som en gång. Två DELETE av samma resurs lämnar den borttagen, medan två POST kan skapa två resurser.

Statuskoden är ett tresiffrigt tal där första siffran anger vilken sorts svar det är:

Klass Betydelse
1xx Information. Den du möter är 101 Switching Protocols, när en anslutning byter från HTTP till WebSocket.
2xx Det gick bra
3xx Omdirigering: resursen finns någon annanstans
4xx Klienten gjorde fel
5xx Servern gjorde fel

De du oftast möter:

Kod Betydelse
200 OK Allt gick bra
201 Created En ny resurs skapades
204 No Content Det gick bra, men svaret har ingen body
301 Moved Permanently Resursen har flyttat för gott. Den nya adressen står i Location.
302 Found och 303 See Other Gå till adressen i Location, till exempel efter ett formulär
304 Not Modified Din cachade kopia gäller fortfarande
400 Bad Request Förfrågan är felaktig
401 Unauthorized Du behöver logga in
403 Forbidden Du är inloggad, men har inte behörighet
404 Not Found Resursen finns inte
405 Method Not Allowed Resursen finns, men inte med den metoden
500 Internal Server Error Något gick fel i servern, ofta ett fel i koden
502 Bad Gateway och 503 Service Unavailable Servern bakom en proxy svarar inte, eller är överbelastad

Headers är metadata om förfrågan och svaret, i formen Namn: värde. Några av de vanligaste:

Header Finns i Betydelse
Host Förfrågan Vilken webbplats förfrågan gäller. Krävs, eftersom många webbplatser kan dela IP-adress.
User-Agent Förfrågan Vilket program som skickar förfrågan
Accept Förfrågan Vilka format klienten förstår, till exempel text/html eller application/json
Content-Type Båda Formatet på bodyn
Content-Length Båda Bodyns storlek i byte
Location Svar Adressen att gå vidare till vid en omdirigering
Cache-Control Båda Om och hur länge svaret får cachas
Set-Cookie Svar Ber klienten spara en cookie
Cookie Förfrågan Skickar med klientens sparade cookies
Authorization Förfrågan Inloggningsuppgifter, till exempel en token

Data skickas i querysträngen eller i bodyn. Content-Type talar om hur bodyn är formaterad:

Content-Type Exempel Används av
application/x-www-form-urlencoded namn=Ada&sprak=JavaScript Vanliga HTML-formulär
multipart/form-data Delar åtskilda av en gränssträng Formulär som laddar upp filer
application/json {"namn":"Ada"} API:er och fetch från JavaScript

Querysträngen syns i adressfältet, i webbläsarens historik och i serverns loggar. Känslig data som lösenord skickas därför alltid i bodyn, med POST.

En cookie är ett litet namngivet värde som servern ber webbläsaren spara med headern Set-Cookie. Webbläsaren skickar sedan tillbaka det i headern Cookie i varje förfrågan till samma webbplats. Så kan en tillståndslös server ändå känna igen en användare: vid inloggningen skapar servern en session, sparar vem användaren är och skickar ett slumpmässigt sessions-id i en cookie.

Set-Cookie: session=4f2a9c...; HttpOnly; Secure; SameSite=Lax; Max-Age=3600
Attribut Betydelse
HttpOnly JavaScript på sidan kan inte läsa cookien, vilket skyddar mot stöld via XSS
Secure Cookien skickas bara över HTTPS
SameSite Om cookien skickas med förfrågningar från andra webbplatser, vilket skyddar mot CSRF
Max-Age eller Expires Hur länge cookien lever. Utan dem försvinner den när webbläsaren stängs.

Sessioner, XSS och CSRF återkommer i kursen, bland annat i vecka 5 om webbsäkerhet.

HTTPS är HTTP inuti en krypterad TLS-anslutning. Krypteringen gör att ingen på vägen kan läsa eller ändra det som skickas, till exempel lösenord och cookies. Men kryptering hjälper inte om du pratar med fel server. Därför visar servern upp ett certifikat, som:

  • innehåller serverns domännamn och dess publika nyckel
  • är signerat av en certifikatutfärdare (certificate authority, CA) som webbläsaren litar på
  • gäller en begränsad tid, ofta några månader

Webbläsaren kontrollerar att certifikatet gäller för rätt domän, att det inte har gått ut och att signaturen leder till en betrodd CA. Serverns privata nyckel får aldrig lämna servern. Den som har den kan utge sig för att vara servern. Du sätter upp certifikat för din egen server senare i kursen.

Webbläsarens utvecklarverktyg (F12, eller Cmd+Option+I på macOS) har fliken Network (Nätverk), som visar varje förfrågan en sida gör, med metod, status, headers, body och tid.

curl gör HTTP-förfrågningar från terminalen:

Kommando Vad det gör
curl <url> Hämtar och skriver ut bodyn
curl -i <url> Visar också svarets headers
curl -I <url> Gör en HEAD-förfrågan och visar bara headers
curl -v <url> Visar allt: förfrågan (>), svar (<) och anslutningen (*)
curl -L <url> Följer omdirigeringar
curl -d 'a=1&b=2' <url> Skickar ett formulär med POST
curl -X DELETE <url> Använder en annan metod
curl -H 'Accept: application/json' <url> Lägger till en header
curl -c fil -b fil <url> Sparar och skickar cookies i en fil, som en webbläsare

Tidsåtgång: Ungefär en och en halv timme för alla nio övningarna.

När du har gått igenom sidan kan du pröva dina kunskaper med övningarna nedan. Gör dem i ordning, eftersom flera bygger på varandra. Försök själv innan du tittar på lösningsförslaget.

Tidsåtgång: Ungefär 10 minuter.

Öppna utvecklarverktygen, välj fliken Network och ladda om kursens startsida. Hur många förfrågningar gör sidan? Vilken Content-Type har sidan själv, och vilken har en CSS-fil? Högerklicka på kolumnrubrikerna och slå på kolumnen Protocol. Vilken HTTP-version används? Jämför med www.google.com.

Sidan gör många förfrågningar: först själva HTML-sidan och sedan CSS, JavaScript, typsnitt och bilder som HTML-sidan hänvisar till. HTML-sidan har text/html och CSS-filerna text/css.

Kurswebbplatsen svarar med http/1.1, medan Google använder h2 (HTTP/2) eller h3 (HTTP/3). Med HTTP/2 och 3 kan många förfrågningar dela en enda anslutning, vilket gör sidor med många resurser snabbare.

Tidsåtgång: Ungefär 5 minuter.

Hämta kursens startsida med curl -v. Vilka rader är förfrågan och vilka är svaret? Vilka headers skickade curl utan att du bad om det?

Terminal window
curl -v https://coursepress.lnu.se/kurser/webbprogrammering-pa-serversidan/

Rader som börjar med > är förfrågan, rader med < är svaret och rader med * beskriver anslutningen, till exempel TLS. curl skickar själv Host, User-Agent och Accept. Host krävs, eftersom många webbplatser kan dela samma IP-adress.

Tidsåtgång: Ungefär 5 minuter.

Gör en HEAD-förfrågan till http://coursepress.lnu.se (utan s). Vilken statuskod får du, och vart skickas du? Följ omdirigeringarna till slutet.

Terminal window
curl -I http://coursepress.lnu.se
curl -IL http://coursepress.lnu.se

Servern svarar 301 Moved Permanently med Location: https://coursepress.lnu.se/. All trafik flyttas till HTTPS. Med -L följer curl efter, precis som webbläsaren gör automatiskt.

Tidsåtgång: Ungefär 10 minuter.

HTTP/1.1 är text som skickas över en TCP-anslutning. Skapa filen raw-http.mjs, som öppnar en TCP-anslutning till port 80 och skriver en förfrågan för hand. Kör den med node raw-http.mjs. Vad händer om du tar bort raden med Host?

import { connect } from 'node:net'
const host = 'coursepress.lnu.se'
const socket = connect(80, host, () => {
socket.write(
'GET /kurser/webbprogrammering-pa-serversidan/ HTTP/1.1\r\n' +
`Host: ${host}\r\n` +
'Connection: close\r\n' +
'\r\n'
)
})
socket.setEncoding('utf8')
socket.on('data', (chunk) => process.stdout.write(chunk))

Svaret är ren text: statusraden HTTP/1.1 301 Moved Permanently, headers, en tom rad och en kort HTML-body. Det är samma svar som i övning 3.

Utan Host svarar servern 400 Bad Request, eftersom HTTP/1.1 kräver headern. \r\n är radslutet som HTTP föreskriver, och den tomma raden sist talar om att förfrågans headers är slut. Connection: close ber servern stänga anslutningen efter svaret, så att programmet avslutas.

Tidsåtgång: Ungefär 10 minuter.

Skapa filen server.mjs med koden nedan och starta den med node server.mjs. Öppna localhost:3000 i webbläsaren och hämta samma adress med curl. Jämför vad servern skriver ut för de två förfrågningarna. Vilka headers skiljer sig åt?

import { createServer } from 'node:http'
const server = createServer((req, res) => {
console.log(req.method, req.url, req.headers)
let body = ''
req.on('data', (chunk) => { body += chunk })
req.on('end', () => {
if (body) console.log('Body:', body)
if (req.url !== '/') {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' })
res.end('Hittades inte\n')
return
}
// The server remembers nothing between requests; the cookie carries the count.
const visits = Number(req.headers.cookie?.match(/visits=(\d+)/)?.[1] ?? 0) + 1
res.writeHead(200, {
'Content-Type': 'text/plain; charset=utf-8',
'Set-Cookie': `visits=${visits}; HttpOnly; SameSite=Lax`
})
res.end(`Hej! Det här är besök nummer ${visits}.\n`)
})
})
server.listen(3000, '127.0.0.1', () => {
console.log('Lyssnar på http://localhost:3000')
})

Webbläsaren skickar många fler headers än curl: en längre User-Agent, en Accept som föredrar text/html, Accept-Language med dina språkinställningar, Accept-Encoding för komprimering och ofta Sec-Fetch-*-headers som beskriver vad förfrågan gäller. Webbläsaren gör också ofta en extra förfrågan till /favicon.ico, som får 404.

Tidsåtgång: Ungefär 10 minuter.

Ladda om sidan i webbläsaren några gånger. Räknaren ökar. Hämta sedan sidan med curl två gånger. Varför står det alltid besök nummer 1? Få curl att räkna uppåt, som webbläsaren. Var hittar du cookien i utvecklarverktygen?

Terminal window
curl http://localhost:3000
curl http://localhost:3000
curl -c kakor.txt -b kakor.txt http://localhost:3000
curl -c kakor.txt -b kakor.txt http://localhost:3000

Servern minns ingenting. Den läser räknaren från cookien i förfrågan och skickar tillbaka ett nytt värde i Set-Cookie. Webbläsaren sparar cookien och skickar den med nästa gång, men curl gör det bara om du ber den med -c (spara) och -b (skicka).

I Chrome och Edge finns cookien under fliken Application, i Firefox under Storage. Att den är HttpOnly betyder att document.cookie i konsolen inte visar den. Pröva!

Tidsåtgång: Ungefär 10 minuter.

Skicka ett formulär med namn=Ada&sprak=JavaScript till servern med curl. Vilken metod och vilken Content-Type använde curl, och hur ser bodyn ut i serverns utskrift? Skicka sedan en DELETE-förfrågan till / och till /finns-inte. Vilka statuskoder får du?

Terminal window
curl -d 'namn=Ada&sprak=JavaScript' http://localhost:3000
curl -i -X DELETE http://localhost:3000
curl -i -X DELETE http://localhost:3000/finns-inte

-d gör att curl använder POST med Content-Type: application/x-www-form-urlencoded, precis som ett HTML-formulär. Bodyn kommer fram som texten namn=Ada&sprak=JavaScript.

DELETE / ger 200, eftersom servern inte bryr sig om metoden, medan /finns-inte ger 404.

Tidsåtgång: Ungefär 15 minuter.

Servern i övning 7 svarar 200 på DELETE /, fast den inte kan ta bort något. Ändra servern så att den svarar 405 Method Not Allowed på alla metoder utom GET och POST. Svaret ska ha headern Allow, som talar om vilka metoder som är tillåtna. Kontrollera med curl -i.

Lägg till kontrollen efter den som ger 404:

if (req.method !== 'GET' && req.method !== 'POST') {
res.writeHead(405, {
'Content-Type': 'text/plain; charset=utf-8',
Allow: 'GET, POST'
})
res.end('Metoden stöds inte\n')
return
}
Terminal window
curl -i -X DELETE http://localhost:3000
# HTTP/1.1 405 Method Not Allowed
# Allow: GET, POST

Ordningen spelar roll: först avgörs om resursen finns (404), sedan om metoden är tillåten för den (405). Rätt statuskoder gör att klienter, verktyg och andra utvecklare förstår vad som hände utan att läsa texten i svaret. I kursens ramverk, Express, sköts mycket av det här åt dig, men det är samma HTTP under ytan.

Tidsåtgång: Ungefär 5 minuter.

Klicka på ikonen till vänster om adressen i webbläsaren när du är på kurswebbplatsen och öppna certifikatet. För vilket domännamn gäller det, vem har utfärdat det och när går det ut?

Certifikatet gäller coursepress.lnu.se, eller flera namn inklusive det. Utfärdaren är en CA som webbläsaren litar på, och certifikatkedjan visar hela vägen upp till en rotutfärdare som finns inbyggd i webbläsaren eller operativsystemet. Certifikat gäller numera sällan längre än ett år, och förnyas därför automatiskt på de flesta servrar.

Kursens inspelning om HTTP från 2021 finns kvar i tidigare kursomgångar.

CCBY