Foundations

HTTP, TCP/IP, DNS, TLS, jak vlastně teče request.

Co se naučíš: Budeš vědět, co se přesně stane mezi kliknutím a odpovědí serveru, a přestaneš hádat, kde request umřel.

17 min čtení + cvičení

Než napíšeš jediný řádek backendu, musíš rozumět tomu, co se vlastně stane, když někdo otevře tvůj web. Zadáš adresu, zmáčkneš Enter a za zlomek vteřiny se načte stránka. Mezitím proběhne několik kroků na několika různých technologiích. Tahle kapitola je projde všechny, pomalu a od nuly. Je to úplný základ: odkazuje se na něj každá další kapitola, tak ho neproskakuj.


Pár pojmů na úvod (ať se neztratíš)

Backend je program, který běží na nějakém počítači připojeném k internetu (tomu počítači se říká server) a odpovídá na požadavky. Ten, kdo se ptá, prohlížeč, mobilní aplikace, je klient.

  • Request (požadavek) = zpráva od klienta serveru: „pošli mi domovskou stránku", „ulož tuhle objednávku".
  • Response (odpověď) = zpráva, kterou server pošle zpátky: stránka, data, nebo hlášení o chybě.
  • Protokol = dohodnutá pravidla, jak spolu klient a server mluví, aby si rozuměli (např. HTTP).
  • IP adresa = číselná adresa počítače v síti, podobně jako adresa domu. Třeba 104.21.5.7.
  • Port = číslo „dveří" na tom počítači. Na jedné IP může běžet víc služeb a každá poslouchá na svém portu (web má výchozí porty 80 pro HTTP a 443 pro HTTPS, proto je v URL nevidíš; ale je to jen konvence, lokální dev běží třeba na 3000 nebo 8080).

Celý backend ve své podstatě dělá jen tohle: přišel request → něco s ním udělej → vrať response. Pořád dokola, klidně milionkrát za den. Všechno ostatní v téhle učebnici je jen rozpracování téhle jedné věty.


Co se stane, když zadáš adresu (mentální model)

Když do prohlížeče napíšeš adresu a dáš Enter, proběhnou zhruba tyhle kroky, než vůbec uvidíš stránku:

Nelekej se pojmů v diagramu, projdeme je teď jeden po druhém v tomhle pořadí: DNS → TCP → TLS → HTTP. Až to dočteš, vrať se sem a diagram ti dá smysl celý. (Reverse proxy, CDN a load balancer si vysvětlíme níže a hlavně v dalších kapitolách, zatím ber, že je to „pár serverů, které stojí před tvojí aplikací".)


1. DNS, překlad jména na adresu

Lidi si pamatují jména (google.com), ale počítače se v síti hledají podle IP adres. DNS (Domain Name System) je telefonní seznam internetu: dáš mu jméno a vrátí ti IP adresu.

Takže úplně první věc, kterou prohlížeč udělá, není stažení stránky, je to dotaz: „jakou IP adresu má google.com?" Teprve když zná IP, ví, kam se vůbec připojit.

  • Odpovědi se cachují (dočasně ukládají) na spoustě míst, v prohlížeči, v operačním systému, u tvého poskytovatele internetu, aby se nemusel stejný překlad dělat znovu a znovu.
  • TTL (Time To Live) říká, jak dlouho se smí odpověď cachovat, než si ji musí vyžádat znovu. Krátké TTL = změny se projeví rychle, ale je víc dotazů. Dlouhé TTL = míň dotazů, ale změna se „rozjíždí" pomaleji.
  • Kdo se koho ptá: tvůj počítač se ptá rekurzivního resolveru (u poskytovatele / veřejného jako 1.1.1.1), a ten teprve obejde hierarchii root → TLD (.cz) → autoritativní server domény. Cache je hlavně na tom resolveru, proto změnu na registrátorovi „nevidíš" hned.
  • Typický zmatek začátečníka: přesměruješ doménu jinam a „nefunguje to", přitom se ti jen ještě chvíli servíruje stará IP z cache. Není to bug, je to TTL. A pozor, cachují se i negativní odpovědi (NXDOMAIN), takže „přidal jsem záznam a chvíli to nešlo" je taky normální. (Příkaz dig example.com v terminálu ti ukáže, jakou IP doména právě vrací.)

Pro úplnost, druhy DNS záznamů (zatím stačí vědět, že existují): A = IPv4 adresa, AAAA = IPv6 adresa, CNAME = „tahle doména je přezdívka jiné", MX = kam doručovat e-maily, TXT = textová poznámka (často pro ověření vlastnictví domény).

  • Praktická pastička při prvním deployi: CNAME nesmí být na apexu (root doméně), tj. na holém example.cz, jen na poddoméně jako www.example.cz. Na apex patří A/AAAA (nebo provider-specific „ALIAS/ANAME"). Klasická chyba, na kterou narazí skoro každý, kdo poprvé směruje doménu.

2. TCP, navázání spolehlivého spojení

Teď, když prohlížeč zná IP, musí se k serveru připojit. Data se po internetu posílají v malých kouscích zvaných pakety, představ si dlouhý dopis rozstříhaný na pohlednice, které putují každá zvlášť a můžou dorazit v jiném pořadí (nebo se cestou ztratit).

  • IP se stará jen o doručení paketů z bodu A do bodu B, bez záruky, že dorazí všechny a ve správném pořadí.
  • TCP je vrstva nad IP, která tu nespolehlivost schová: pakety seřadí, ztracené pošle znovu a zařídí, aby na druhé straně vyšel kompletní, správně poskládaný proud dat. Proto se mu říká spolehlivý protokol.

Než si spolu dva začnou „povídat", musí se nejdřív pozdravit, tomu se říká handshake:

Klient:  „Slyšíš mě?"        (SYN)
Server:  „Jo, a ty mě?"      (SYN-ACK)
Klient:  „Jo, jedeme."       (ACK)

Tenhle pozdrav stojí jeden round-trip (cesta tam a zpět), a to je čas. Proto se navázaná spojení snaží znovupoužívat místo otevírání nového na každý request. Tahle drobnost je zdrojem překvapivě velké části výkonových problémů, třeba otevírat nové spojení do databáze na každý požadavek je „malá" chyba s velkým dopadem (víc v kapitole Concurrency a Performance & Scaling).

UDP je bratříček TCP, který tyhle záruky nemá, pakety prostě vystřelí a nestará se, jestli dorazily. Zní to špatně, ale pro video hovory, hry nebo právě DNS je to výhra: radši rychle a občas ztratit paket než čekat na potvrzení. Klíčová myšlenka: vědět, kdy obětovat spolehlivost za rychlost.


3. TLS / HTTPS, zašifrování a ověření

To „s" v https znamená secure. Bez něj by si kdokoli na cestě mezi tebou a serverem (Wi-Fi v kavárně, tvůj poskytovatel) mohl přečíst i změnit, co posíláš. TLS to řeší dvěma věcmi naráz:

  1. Šifrování, data putují jako v zalepené obálce, kterou po cestě nikdo nepřečte.
  2. Ověření identity, server se prokáže certifikátem, takže máš jistotu, že mluvíš se skutečným mojebanka.cz, a ne s podvrženým serverem útočníka. Certifikátu věříš proto, že ho podepsala důvěryhodná autorita (CA), které věří už tvůj prohlížeč.

Tohle ověření je další handshake a stojí další round-tripy (novější TLS 1.3 to zkrátil). Je to další důvod, proč je drahé navazovat spojení pořád znovu.

Časté problémy, na které narazíš: expirovaný certifikát (platnost mají omezenou, musí se obnovovat) nebo špatně nakonfigurovaný řetěz certifikátů, prohlížeč pak ukáže to varování o „nedůvěryhodném připojení".


4. HTTP, vlastní jazyk requestů

Konečně se dostáváme k samotné konverzaci. HTTP je protokol, kterým si klient a server řeknou, co chtějí. Každý request má:

  • metodu (co chci dělat, viz níže),
  • cestu (/courses/42, který zdroj),
  • hlavičky (headers, doplňující informace, např. v jakém formátu chci data),
  • volitelně tělo (body, data, která posílám, třeba obsah nové objednávky).

A každá response má status kód (jak to dopadlo), hlavičky a tělo (data nebo HTML).

„Stateless", server tě po každém requestu zapomene

HTTP je stateless (bezstavový): server si mezi jednotlivými requesty nic nepamatuje. Každý request musí přijít se vším kontextem, který server potřebuje, hlavně „kdo jsem". Proto s každým požadavkem posíláš znovu cookie nebo token, je to jako ukazovat lístek pokaždé, když projdeš turniketem, protože tě obsluha mezi průchody nepozná.

Zní to jako nevýhoda, ale je to obrovská výhoda: když si server nedrží žádný osobní stav, může tvůj request obsloužit kterýkoli ze serverů, a to je přesně to, co umožňuje přidávat servery a škálovat (víc o tom v Performance & Scaling).

Metody, co chci s daty dělat

MetodaCo znamenáBezpečná (jen čte)?Idempotentní?
GETpřečti zdroj✅✅
POSTvytvoř / proveď akci❌❌
PUTnahraď celý zdroj❌✅
PATCHuprav část zdroje❌záleží
DELETEsmaž zdroj❌✅

U PATCH „záleží" znamená podle toho, co děláš: PATCH {stav: "zaplaceno"} (nastavení na pevnou hodnotu) je idempotentní, ale PATCH {pocet: pocet + 1} (přičtení) idempotentní není.

Idempotentní je slovo, které uvidíš v téhle učebnici pořád, tak hned na začátku jednoduše: operace je idempotentní, když ji provedeš dvakrát a výsledek je stejný jako jednou. Smazat objednávku #5 dvakrát = pořád je smazaná (idempotentní). Ale poslat „zaplať 500 Kč" dvakrát = dvě platby (NEidempotentní!). Proto se u plateb řeší speciálně, viz Patterny.

Status kódy, jak to dopadlo

Server v každé odpovědi vrací třímístné číslo. Stačí znát skupiny:

  • 2xx = povedlo se. 200 OK, 201 vytvořeno, 204 OK ale bez dat.
  • 3xx = přesměrování. 301 trvale jinam, 302 dočasně jinam, 304 „nezměnilo se, použij cache". (Past: po 301/302 může prohlížeč změnit POST na GET; 307/308 původní metodu zachovají.)
  • 4xx = chyba na straně klienta (request je špatně). 400 špatný request, 401 nepřihlášen, 403 přihlášen, ale nemá právo, 404 neexistuje, 409 konflikt, 422 neprošla validace, 429 moc requestů (rate limit).
  • 5xx = chyba na straně serveru (server to pokazil). 500 interní chyba, 502/503/504 problémy s nedostupností nebo timeoutem (odpověď nepřišla včas, viz dole u TCP).

Nejčastější záměna: 401 vs 403. 401 = „nevím, kdo jsi" (přihlas se). 403 = „vím, kdo jsi, ale na tohle nemáš právo". Plést si je je klasická juniorská chyba.

Hlavičky, které potkáš

Hlavičky jsou doplňkové info ke každému requestu/response. Pár, které uvidíš pořád: Content-Type (v jakém formátu jsou data, např. JSON), Authorization (token, kterým se prokazuješ), Cache-Control (jak dlouho se smí odpověď cachovat, viz Caching).

Verze HTTP (jen ať víš, že existují)

HTTP/1.1 je stará klasika. Měl jeden ošklivý problém: po jednom spojení šel naráz jen jeden request, takže prohlížeč musel pro paralelní stahování otevírat víc TCP spojení (a každé stálo handshake, plýtvání). HTTP/2 to vyřešil, víc požadavků přes jedno spojení naráz (multiplexing). Jenže běží nad TCP, kde ztráta jednoho paketu pozdrží všechny streamy najednou (head-of-line blocking se z úrovně requestů jen přesunul níž, na úroveň TCP). A přesně tenhle TCP-level HOL blocking řeší HTTP/3 tím, že běží nad UDP (QUIC), ztráta paketu zdrží jen ten jeden stream, ne ostatní. To je celý důvod jeho existence. Jako junior nemusíš řešit detaily, ale tahle pointa „proč HTTP/3" stojí za zapamatování.


Cookies a sessions, jak si tě server „pamatuje"

Řekli jsme, že HTTP je stateless a server tě po každém requestu zapomene. Jak tedy zůstaneš přihlášený? Přes cookie, malý kousek dat, který server pošle prohlížeči, a ten ho pak automaticky přikládá ke každému dalšímu requestu na ten web. Funguje to dvěma způsoby:

  • Session cookie: v cookie je jen náhodné ID session, podle kterého si server v paměti/databázi najde, kdo jsi. Stav je na serveru. (Pozor: to ID je samo o sobě tajemství, kdo ho ukradne, je přihlášený za tebe, tomu se říká session hijacking. Proto HttpOnly níže.)
  • Token v cookie: v cookie je rovnou token, který nese informaci o tobě (detaily v Security).

Zásadní rozdíl mezi session a self-contained tokenem (JWT) je zneplatnění: session jde okamžitě zneplatnit smazáním záznamu na serveru, kdežto JWT typicky platí až do svého vypršení, takže odhlášení a revokace jsou těžké (viz Security).

U cookies si pak hlídáš tři bezpečnostní nastavení (víc v Security): HttpOnly (cookie nepřečte JavaScript → ochrana před krádeží), Secure (posílá se jen po HTTPS) a SameSite (omezí, kdy se cookie přikládá → ochrana před CSRF).


Cachování a komprese, ať se nepřenáší zbytečně

Dvě hlavičky, kterými server šetří přenos (víc v Caching):

  • Cache-Control říká prohlížeči (a CDN), jak dlouho si smí odpověď schovat, aby ji nestahoval pořád dokola.
  • ETag + If-None-Match je „otisk" verze obsahu. Prohlížeč při dalším dotazu pošle otisk, který má; když se nezměnil, server odpoví krátkým 304 Not Modified místo posílání celých dat znovu.

A komprese (gzip/brotli): server odpověď před odesláním zmenší a prohlížeč ji rozbalí, menší data putují sítí rychleji. Většinou to zapne server sám.


CORS, proč ti volání projde v terminálu, ale ne v prohlížeči

Tohle tě dřív nebo později „kousne", tak ať víš dopředu. CORS je bezpečnostní pravidlo prohlížeče (ne tvého serveru!). Prohlížeč z principu nedovolí, aby JavaScript na webu A.cz volal server na B.cz, pokud to server B.cz výslovně nepovolí speciální hlavičkou.

Důsledek, který mate každého: úplně stejný požadavek projde z terminálu (příkazem curl), ale v prohlížeči spadne na CORS chybu. Není to bug v tvém kódu, curl totiž žádné takové bezpečnostní pravidlo nemá, kdežto prohlížeč ano. Řešení je doplnit na serveru správné CORS hlavičky.


Failure modes, jak to v praxi „praská"

Tyhle tři chyby vypadají podobně, ale každá znamená něco jiného. Umět je rozlišit = umět rychle najít příčinu:

  • Connection refused: na druhé straně nikdo neposlouchá (služba neběží, nebo voláš špatný port).
  • Timeout: spojení se navázalo, ale odpověď nepřišla včas (server je přetížený, nebo na něčem zaseknutý).
  • Connection reset: spojení bylo násilně přerušeno (něco po cestě ho zabilo, server spadl).

A jedna past, na kterou tě připraví zbytek učebnice: slepě opakovat (retry) neidempotentní POST po timeoutu může vést k duplicitní akci, třeba dvě platby. Proto se requesty a opakování musí navrhovat s rozmyslem (viz Patterny → Idempotency).


🛠️ Cvičení

Pár cvičení používá nástroje, které možná vidíš poprvé (DevTools v prohlížeči, curl, dig v terminálu). To je záměr, nejlíp se to naučíš tím, že si na ně sáhneš.

  1. Sleduj reálný request. V prohlížeči otevři vývojářské nástroje (F12) → záložka Network, načti libovolný web a najdi: status kód, hlavičku Content-Type a (v záložce Timing) jak dlouho trval TLS handshake.
  2. Zkus dig a curl. V terminálu spusť dig example.com (uvidíš IP adresu) a curl -v https://example.com (uvidíš handshake i hlavičky requestu a response). Najdi v outputu status kód.
  3. Vyrob si CORS chybu. Na libovolném webu otevři konzoli (F12 → Console) a napiš fetch('https://example.org'). Přečti chybovou hlášku a vysvětli, proč by stejné volání přes curl prošlo.
  4. Zařaď status kódy. Ke každé situaci přiřaď kód: nepřihlášený uživatel, přihlášený bez práv, neexistující stránka, neprošla validace formuláře, spadl server, příliš mnoho requestů.
  5. Idempotence v praxi. U každého rozhodni, jestli je bezpečné poslat to dvakrát, a proč: GET /orders/5, DELETE /orders/5, POST /orders, PUT /orders/5.
Náčrt řešení: rozbal, až si cvičení zkusíš sám
  1. Sleduj reálný request, v Network panelu klikni na konkrétní request, status najdeš v záhlaví, Content-Type v Response headers a TLS čas v záložce Timing („SSL"). Pozor: TLS čas uvidíš jen u prvního spojení, další requesty ho znovupoužijí (proto je 0), tak hledej ten první, na který se navazuje.
  2. Zkus dig a curl, dig ti vypíše A záznam (IP), curl -v ukáže celý handshake i hlavičky; status kód je první řádek odpovědi (HTTP/2 200). Na co pozor: řádky začínající * jsou diagnostika spojení (DNS/TCP/TLS), > je tvůj request, < je response, neplést si je.
  3. Vyrob si CORS chybu, fetch spadne, protože example.org nepošle hlavičku Access-Control-Allow-Origin, takže prohlížeč odpověď zablokuje. Klíčová pointa: blokuje prohlížeč, ne server, curl žádné CORS pravidlo nemá, proto stejné volání projde. Nejde o chybu v kódu, ale o chybějící povolení na serveru.
  4. Zařaď status kódy, nepřihlášený 401, přihlášený bez práv 403, neexistující stránka 404, neprošlá validace 422, spadlý server 500, příliš requestů 429. Klasická past: nepřihlášenému vracet 403 místo 401 (401 = „nevím kdo jsi", 403 = „vím, ale nesmíš").
  5. Idempotence v praxi, GET, PUT i DELETE jdou opakovat bez vzniku duplicit (jsou idempotentní), POST /orders ne (každé volání vytvoří novou objednávku). Pozor: nepleť „idempotentní" se „safe", safe je jen metoda, co vůbec nemění stav (GET); PUT/DELETE stav mění, jen opakování nepřidá nic navíc. DELETE je idempotentní co do stavu (po prvním je smazáno), i když druhé volání může vrátit 404, to nevadí.

🧠 Otázky & odpovědi

Proč volání projde v curlu, ale v prohlížeči spadne na CORS?

CORS je bezpečnostní pravidlo prohlížeče, ne vlastnost serveru. Prohlížeč zakáže JavaScriptu volat jiný web, dokud to ten web výslovně nepovolí hlavičkou (Access-Control-Allow-Origin). curl v terminálu žádné takové pravidlo nemá, takže prostě pošle požadavek. Proto „v curlu to jede, v appce ne", a proto to není chyba v tvém kódu, ale chybějící povolení na serveru.

Jaký je rozdíl mezi 401 a 403?

401 Unauthorized = „nevím, kdo jsi", chybí nebo je neplatné přihlášení, takže se přihlas. 403 Forbidden = „vím, kdo jsi, ale na tohle nemáš právo", jsi přihlášený, ale nemáš oprávnění. Častá chyba je vracet 403 tam, kde je uživatel jen nepřihlášený.

Co znamená, že je HTTP stateless, a proč je to výhoda?

Server si mezi requesty nic nepamatuje, každý požadavek musí přinést všechen kontext sám (kdo jsem → cookie/token). Výhoda: protože si žádný server nedrží „tvůj" stav, může tě obsloužit kterýkoli z nich. Stav se ukládá mimo (do databáze nebo cache), takže když přidáš další servery, hned zvládneš víc uživatelů. To je základ horizontálního škálování.

Proč je drahé otevírat nové spojení a co s tím?

Navázat spojení znamená udělat TCP handshake a (u HTTPS) i TLS handshake, to jsou cesty tam a zpět, tedy čas, plus alokace zdrojů. Když otevíráš nové spojení na každý požadavek (třeba do databáze), platíš tu cenu pokaždé a navíc můžeš vyčerpat limit spojení. Řešení je spojení znovupoužívat (connection pooling, drží se sada otevřených spojení a recyklují se).

Rozliš connection refused, timeout a connection reset.

Refused = na druhé straně nikdo neposlouchá (služba neběží nebo voláš špatný port). Timeout = spojení se navázalo, ale odpověď nepřišla včas (přetížení, zaseknutí). Reset = spojení bylo násilně přerušeno (něco po cestě ho ukončilo, server spadl). Každé ukazuje na jinou příčinu, takže je neházej do jednoho pytle, rozlišení ti ušetří hodiny debugování.

Když je HTTP stateless, jak zůstanu přihlášený?

Přes cookie, malý kousek dat, který server pošle prohlížeči a ten ho automaticky přikládá ke každému dalšímu requestu na ten web. V cookie bývá buď ID session (server si podle něj najde, kdo jsi), nebo rovnou token nesoucí informaci o tobě. Server tě tak při každém requestu „pozná", aniž by si mezi nimi cokoli pamatoval.

Co je ETag a co znamená 304 Not Modified?

ETag je „otisk" verze obsahu, který server pošle s odpovědí. Při dalším dotazu prohlížeč ten otisk přiloží zpátky; když se obsah nezměnil, server místo posílání celých dat odpoví krátkým 304 Not Modified = „nic nového, použij, co máš v cache". Šetří to přenos i čas, nestahuje se znovu něco, co se nezměnilo.