Geschrieben von

Wie Webseiten geladen werden

WebDev

Aus meiner Betrachtungsweise gibt es – angefangen bei der Eingabe der Webadresse in die Adresszeile des Browsers bis zum Fertigladen der Seite im Browser – 2 grobe Phasen beim Laden einer Webseite:

  • Client-Server-Kommunikation
  • Darstellung der Webseite im Browser

Im Nachfolgenden gehe ich auf die Schritte dieser 2 Phasen ein. Einzelne wichtige Schritte und Begriffe erkläre ich zum besseren Verständnis nochmal eingehender weiter unten. Dieser Artikel wird jedoch den Seitenaufbau nicht im Detail erklären, sondern soll eine vereinfachte Übersicht geben, was grob im Hintergrund passiert, wenn man eine Webadresse aufruft.

Client-Server-Kommunikation

Die Client-Server-Kommunikation basiert auf Netzwerkprotokollen. Für die Kommunikation im Internet kommt eine Internetprotokollfamilie zum Einsatz. Dies ist – einfach ausgedrückt – eine Sammlung verschiedener Netzwerkprotokolle. Da die eingesetzten Protokolle verschiedene Aufgaben während der Kommunikation übernehmen, werden sie in Schichten gruppiert. Für die Einordnung von Internetprotokollen wird das TCP/IP-Referenzmodell mit 4 aufeinander aufbauenden Schichten verwendet:

Nr.SchichtBeispiele
1NetzzugangEthernet, Token Bus, Token Ring, FDDI
2InternetIP (IPv4, IPv6), ICMP (über IP)
3TransportTCP, UDP, SCTP
4AnwendungHTTP, FTP, SMTP, POP, Telnet, SOCKS

Dieses Modell ist auf die Kommunikation im Internet zugeschnitten. Die Netzzugangsschicht (Link Layer) bietet Platz für die physische Datenübertragung und Zugriffskontrollen. Dabei geht es darum, wie Daten über Kabeln, Glasfasern oder Funksignalen übertragen wird (WLAN, Ethernet, etc.). Außerdem regeln Zugriffsverfahren, welche Geräte zu welchem Zeitpunkt und an wen Daten übertragen dürfen.

Die Internetschicht (Internet Layer) hat als Hauptaufgabe das Routing (Wegwahl der Daten durch ein Netzwerk). Zudem sind diese Protokolle für die Weiterleitung von Datenpaketen an Zwischenziele verantwortlich, die auf dem Weg zum Empfänger sind. Einfach ausgedrückt: Hier geht es um die Daten-Weiterleitung von Router zu Router. Das bekannteste Protokoll ist dabei IP (Internet Protocol). In einem IP-Netzwerk haben die einzelnen Endgeräte eindeutige IP-Adressen. Über diese können die Geräte adressiert werden. Bei IP-Adressen unterscheidet man in den 2 Versionen IPv4 und IPv6. Während IPv4-Adressen 32 Bits lang sind, beträgt die Länge bei IPv6-Adressen 128 Bits.

Die Transportschicht (Transport Layer) stellt die Ende-zu-Ende-Kommunikation und den Daten-Transport sicher. Zudem zerlegt die Transportschicht die Daten in Datenpakete, sendet diese an den Empfänger und setzt sie wieder zusammen. Durch die IP-Adresse (siehe Internetschicht) weiß die Transportschicht, wo die Datenpakete gesendet werden sollen. Innerhalb der Transportschicht unterscheidet man wiederum zwischen der verbindungslosen und verbindungsorientierten Datenübertragung. Eine Kontrolle, ob die Daten beim Empfänger ankommen, gibt es bei der verbindungslosen Übertragung nicht; bei der verbindungsorientierten schon. TCP (Transmission Control Protocol) ist dabei ein verbindungsorientiertes und zugleich wichtigstes Netzwerkprotokoll. Zusammen mit IP ist TCP/IP die Grundlage des Internets. UDP (User Datagram Protocol) wäre ein Beispiel für ein verbindungsloses Netzwerkprotokoll.

Auf der Anwendungsschicht (Application Layer) finden sich alle Protokolle, die mit Anwendungen direkt zusammenarbeiten. Hier findet der Austausch anwendungsspezifischer Daten statt. Beispiele: E-Mails (mit SMTP), Websites (mit HTTP) oder Dateien (mit FTP).

So viel zur Theorie. In der Praxis besagt das einfache Client-Server-Modell, dass der Client eine Anfrage macht und der Server dann mit z.B. einem HTML-Dokument antwortet. Der grobe Prozess zum Laden einer Webseite sieht dann so aus:

  1. Client fordert Webseite an (Request genannt)
  2. Server antwortet und schickt die Webseite an den Client (Response genannt)

Dazwischen passiert jedoch noch einiges, was wir uns gleich ansehen werden. Davor schauen wir uns die 2 Begriffe “Client” und “Server” an:

  • Als Client werden die Nutzer, Programme und Anwendungen bezeichnet, die mit dem Internet verbunden sind. In unserem Fall wäre der Client, der eine Anfrage an den Server sendet, der Webbrowser wie Chrome, Firefox oder Internet Explorer.
  • Der Server hingegen ist der Speicherort, wo die Webseite liegt. Die Meisten zahlen bei einem Webhoster eine monatliche Gebühr, um deren Server dafür zu nutzen.

Nun schauen wir uns an, was sonst noch zwischen dem Request und Response passiert. Zunächst gehe ich auf den Prozess ein. Weiter unten findest du dann weiterführende Erklärungen und Informationen zu den fett markierten Begriffen.

  1. Der Nutzer öffnet den Browser und gibt eine Domain (Webadresse oder URL) in die Adresszeile ein
  2. Auflösung der Domain in eine IP-Adresse mittels DNS-Server
  3. Client sendet eine HTTP-Anfrage (Request) an den Server
  4. Nutzung von TCP/IP für die Datenübertragung
  5. Server antwortet (Response)
  6. Datenpakete werden an den Client gesendet
  7. Browser verarbeitet die Datenpakete und stellt die Webseite dar

Domain und URL
Wie vorhin erwähnt, muss die Webseite auf einem Server liegen, damit sie gefunden werden kann. Zusätzlich ist aber auch eine Domain notwendig, damit man den Server ansteuern kann. In meinem Fall wäre demirjasarevic.com die Domain. Beim Eingeben dieser Domain in den Browser landet man auf dieser Webseite.

Neben der Domain gibt es aber noch URLs. URL steht für Uniform Resource Locator und gibt an, wo eine Ressourcen gefunden werden kann. Die komplette URL für diese Seite wäre https://www.demirjasarevic.com/web/. https:// wäre dabei das HTTP(S)-Protokoll (dazu später mehr). www.demirjasarevic.com wäre die Domain, wobei das .com die TLD (Top Level Domain) darstellt. Das /web/ am Ende ist der Dateiname, den man mit web.html gleichsetzen kann. In URLs können jedoch noch folgende 3 Elemente enthalten sein:

  • Port: Die Portangabe wird am Ende der Domain mit einem Doppelpunkt und der Portnummer geschrieben. Hier wird mit einer Zahl zwischen 0 und 65535 angegeben, welche Aufgabe der Server übernehmen soll. Z.B. ist 80 der Standardport für HTTP und 443 für HTTPS.
  • Query String: Mit dem Query String werden Parameter an den Server übergeben. Dies wird meist bei dynamischen Inhalten angewendet. In der URL beginnt ein Query String mit einem ? und unterschiedliche Parameter werden mit & getrennt.
  • Hash: Der Hash (oder auch Fragment) – also # – gibt die Sprungmarke innerhalb des Dokuments an.

DNS-Server
Jedes Mal wenn ein Nutzer die Domain eingibt und die Webseite ansteuern will, muss diese zunächst in eine IP-Adresse umgewandelt werden. Warum? Webadressen bestehen eigentlich nur aus Zahlen. Genauer gesagt steht hinter jedem Domainnamen eine IP-Adresse, die einzigartig ist. Da man sich diese IP-Adresse nicht so einfach merken kann, wurden DNS-Server erfunden. DNS steht dabei für Domain Name Server. Aufgabe dieser Server ist es, zu der eingegebenen Domain die passende IP-Adresse zu finden. Im Umkehrschluss heißt das, dass man Webseiten auch über die Eingabe der IP-Adresse in der Browseradressleiste erreichen kann.

HTTP-Protokoll
HTTP steht für Hypertext Transfer Protocol. Mit diesem Protokoll lassen sich Webseiten und die dazu notwendigen Dateien übertragen. Bei HTTPS (“S” steht für Secure) werden die übertragenen Daten verschlüsselt. Nach der DNS-Auflösung sendet der Client eine Anfrage (Request) an den Server. Diese Anfrage beinhaltet einige Informationen und kann folgendermaßen aussehen:
GET /seite.html HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36

Zur Erklärung: GET ist die Request-Art. Diese sagt aus, dass wir uns was vom Server holen wollen. /seite.html ist die relative URL, die wir laden wollen. Danach kommt die Protokollversion (HTTP/1.1). Und als letztes in unserem Beispiel haben wir den HTTP-Header mit dem User-Agent, der uns sagt, welcher Browser und welches Betriebssystem die Anfrage sendet.

Der Server antwortet dann (Response). Die Informationen können dazu wie folgt aussehen:
HTTP 1.1 200 OK
Content-Length: 22345
Content-Type: text/html; charset=UTF-8

Zur Erklärung: Zunächst folgt hier wieder die Protokollversion, gefolgt vom Status Code (200) der Anfrage inkl. der Statusmeldung (OK). Zum Schluss kommen die zwei Header Content-Length (Byte der Antwort) und Content-Type.

Nach dem Response muss es aber noch nicht zu Ende sein. Danach kommen noch CSS-, JS- und Bilddateien, wo sich der Prozess nochmal wiederholt. Aber: HTTP/2 bringt in Zukunft hier Verbesserungen mit sich, wo alle Ressourcen innerhalb einer Anfrage mitgeschickt werden können.

Beim HTTP-Protokoll ist es noch wichtig zu wissen, dass dieses zustandlos ist. Heißt konkret: Mehrere Anfragen von einem Nutzer werden unabhängig voneinander betrachtet. Besucht ein Nutzer eine Webseite und bekommt vom Server eine Antwort, hat der Server den Nutzer bei der nächste Anfrage wieder “vergessen”. Es werden also keine Informationen abgespeichert, damit z.B. der Browser weiß, dass der Nutzer bestimmte Einstellungen auf der Webseite vorgenommen hat (z.B. Login-Status). Diese Aufgabe übernehmen Cookies.

TCP/IP
TCP steht für Transmission Control Protocol und das IP steht für Internet Protocol. Dabei handelt es sich um Kommunikationsprotokolle, auf denen das Internet und die Datenübermittlung basiert. Diese 2 Protokolle legen fest, wie und in welchen Format Client und Server miteinander kommunizieren. Die Kommunikation wird dabei in mehreren Schichten organisiert. Jede dieser Schichten übernimmt unterschiedliche Aufgaben. Neben der Anwendungsschicht (hierzu gehören HTTP und HTTPS) gibt es die Transportschicht (hierzu gehört TCP), Internetschicht (hierzu gehört IP) und Netzwerkschicht.

TCP sorgt dafür, dass es zwischen Client und Server zu einer Verbindung kommt. Hier können dann beide Seiten gegenseitig Informationen austauschen. IP sorgt in diesem Fall, dass durch die eindeutige IP-Adresse die Daten an die richtige Adresse ankommen.

Datenpakete
Bei Datenpaketen handelt es sich um Daten, die in kleineren Stücken von Server zu Client verschickt werden. Dadurch muss der Browser nicht auf ein großes Datenpaket warten, um die Webseite darzustellen, sondern kann kleinere Datenpakete erhalten, um so die Webseite Stück für Stück aufzubauen.

Darstellung der Webseite im Browser

Damit nach der Client-Server-Kommunikation eine Webseite im Browserfenster dargestellt werden kann, muss ein komplexer Prozess durchlaufen werden, sobald das Dokument beim Browser angekommen ist:

  1. Parsing und Erstellung DOM und CSSOM
  2. Erstellung Baumstruktur
  3. Layout
  4. Paint

Parsing und Erstellung DOM und CSSOM

Wie wie vorhin gesehen haben, wird durch die Eingabe der URL in die Adresszeile des Browsers eine Anfrage vom Browser an den Server gestellt. Zunächst erfolgt ein DNS-Lookup. Dabei wird die Domain in eine IP umgewandelt. Der Server antwortet und sendet nun dem Browser das HTML-Dokument. Sobald das HTML-Dokument angekommen ist, muss es nun für den Nutzer mit allen Inhalten und Elementen dargestellt werden.

Dazu analysiert der Browser das HTML und baut daraus das Document Objekt Model (DOM). Folgende Schritte sind dabei notwendig, um die Seite zu verarbeiten:

  • Tokensierung
  • Lexing
  • Erstellung DOM

Zunächst werden die Characters wie <html>, <head>, etc. in Tokens ([StartTag: html], [StartTag: head], etc.) umgewandelt (= Tokensierung). Dieser Prozess wird vom sogenannten Tokenizer übernommen. Anhand der spitzen Klammern der HTML-Characters weiß der Tokenizer wann ein Token beginnt und endet. Auf dieser Basis kann eine sinnvolle Struktur erstellt werden.

Während der Tokenizer diesen Prozess durchläuft, wird gleichzeitig ein anderer Prozess durchgeführt, wo die Tokens in Nodes umgewandelt werden (= Lexing). Wenn alle Nodes stehen und die Beziehungen zueinander definiert worden sind, haben wir das Document Objekt Model (DOM). Das DOM ist also eine Baumstruktur des HTMLs.

Der Vorteil von HTML ist, dass es stufenweise ausgegeben werden kann. Der Browser muss es nicht komplett rendern, um die ersten Elemente sichtbar darzustellen. Bei einer HTML-Seite mit Logo und Navigation im Header können diese sofort nach dem Rendern angezeigt werden, bevor überhaupt der Rest geladen werden kann.

Damit der Browser nun weiß, wie die einzelnen Inhalte dargestellt werden müssen, braucht er das CSS Object Model (CSSOM). Auch hier sind folgende Schritte notwendig:

  • Tokensierung
  • Lexing
  • Erstellung CSSOM

Die CSS-Characters wie

body {font-size: 16px}

p {font-weight: bold}

müssen zunächst in Tokens umgewandelt werden. Danach erfolgt die Umwandlung in Nodes, um daraus dann das CSSOM zu erstellen.

Kann CSS genauso wie HTML vom Browser stufenweise ausgeliefert werden?
Nein, das geht nicht. Nehmen wir an, der Browser rendert die ersten CSS-Teile, die folgende Anweisungen enthalten:

body {font-size: 12px}

p {font-weight: normal}

Mit diesen Anweisungen kann die Seite noch nicht sofort dargestellt werden, da weitere CSS-Anweisungen folgen könnten, die z.B. p {font-weight: normal} durch p {font-weight: bold} ersetzen. Mit CSS ist es möglich, Styles neu zu definieren bzw. werden die Styles nachrangig abgearbeitet (das Prinzip des Cascading). Wir können also nicht wie beim HTML einzelne Bestandteile schon ausspielen, da das CSS noch verändert werden kann. Der Browser blockiert hier das Rendering, bis er das komplette CSS bekommen hat. Deshalb ist CSS standardmäßig “Render-blocking” (wenn man beispielhaft das CSS einer Seite ausschaltet, versteht man auch wieso: Ohne CSS kann die Seite nicht richtig dargestellt werden). Daher macht es Sinn das CSS in 2 Teile zu spalten. Das CSS, welches für den kritischen Rendering-Pfad benötigt wird, kann im Head-Bereich inline geladen werden. Der Rest des CSS kommt in eine separate Datei, welches man mit loadCSS(); asynchron laden kann.

Und was ist mit JavaScript?
JavaScript ist eine dynamische Sprache und gibt uns viele Möglichkeiten. Zum Beispiel kann man damit das DOM und CSSOM verändern. Jedoch bringt der Einsatz von JavaScript auch Herausforderungen mit sich.

Ähnlich wie CSS sind auch JavaScript-Dateien “Render-blocking”. Sobald der Browser auf eine .js-Datei stößt, wird die DOM-Konstruktion angehalten, bis JavaScript zur Verfügung steht. Danach beginnt der Browser wieder an der Stelle, wo er aufgehört hat, weiter zu rendern. Zudem sind Skripte abhängig vom CSSOM. Wenn der Browser das CSS noch nicht vollständig geladen hat, wird die Ausführung des Skripts verzögert, bis das CSSOM vollständig bereit steht. Zusätzlich wird, wie oben beschrieben, die DOM-Erstellung angehalten.

Dabei ist es gleichgültig ob man JavaScript inline oder in einer separaten Datei laden lässt. Inline-Anweisungen haben nur den Vorteil, dass hier der Browser keinen zusätzlichen HTTP-Request starten muss. Externe JS-Dateien haben jedoch den Vorteil, dass die Pflege deutlich einfacher ist.

Es gibt jedoch eine einfache Methode, um JavaScript optimiert laden zu lassen: Das async-Attribut. Dieses Attribut teilt dem Browser mit, dass die Erstellung des DOM nicht gestoppt werden soll, wenn es einem Skript begegnet. Dadurch blockiert das Skript zudem das CSSOM nicht. Wenn das Skript verfügbar ist, bevor das CSSOM bereit ist, kann es trotzdem sofort ausgeführt werden. Aus <script src="app.js"></script> wird dann <script src="app.js" async></script>.

Hinweis: Bei Inline-Skripten schaut das anders aus. Hier kann man async nicht hinzufügen. Inline Skripte werden CSS immer blockieren, außer JavaScript wird vor dem CSS ausgeführt. Das kann jedoch zu Fehlern führen, wenn JavaScript versucht auf CSS-Elemente zuzugreifen, die noch nicht zur Verfügung stehen.

Erstellung Baumstruktur

Sobald DOM (Content der Seite) und CSSOM (Styles der Seite) fertig sind, ergeben sie zusammen die Baumstruktur (Render Tree).

Hinweis: Die Baumstruktur repräsentiert alle sichtbaren Elemente der Seite. Elemente, die per CSS als “display:none” markiert werden, fließen nicht in die Baumstruktur ein.

Layout

Nach der Erstellung der Baumstruktur geht es zum Layout. Hier wird definiert, wie und wo die Elemente auf der Webseite platziert werden (auch Reflow genannt). Das Meta-Tag

<meta name="viewport" content="…">

spielt dabei eine wichtige Rolle. Dieses Tag im Head-Bereich sagt dem Browser, an welcher Größe sich die CSS-Spezifikationen halten sollen. Im Attribut “content” wird die Breite angegeben bzw. auch Eigenschaften wie initial-scale (Zoomgrad), user-scaleable (Zoomen sperren oder erlauben) oder minimum-scale/maximum-scale (Zoomgrad einschränken).

Gibt man im Attribut “content” die Breite “width=device-width” an, so richtet sich die Breite am Ausgabegerät. Wird nun die Breite über das CSS für den Body auf 100 % gesetzt und das Ausgabegerät ist 320 Pixel breit, ergibt das eine Body-Breite von 320 Pixel. Wenn man im Attribut “content” nichts angibt, dann zieht der Browser den Standard-Wert von 980 Pixel heran. Greifen wir dann auf die Seite mit unserem Smartphone mit einer Breite von 320 Pixel zu, müssen wir rein- bzw. rauszoomen, um den Text erfassen zu können. Damit es bei der Darstellung zu keinen Problemen kommt, sollte daher der Viewport stets angegeben werden.

Paint

Im letzten Schritt werden die Pixel am Bildschirm in einem Stacking Context dargestellt.