Was wir gemeinhin als Web bezeichnen, ist eine Anwendung, die auf dem Internet aufsetzt. Das Internet selbst verbindet Computer und ermöglicht ihnen mithilfe grundlegender Protokolle wie IP, Datenpakete auszutauschen. Das Web fügt darauf die Sprache HTTP hinzu, die 1991 eingeführt wurde und festlegt, wie Webseiten und andere Ressourcen angefordert und übertragen werden.
Der einfachste und erste Einsatz von HTTP sind statische Webseiten, das bedeutet, ein Server macht eine fixe Anzahl an Ressourcen (HTML, CSS, Bilder, Fonts, etc.) aus internen Verzeichnissen publik, der Nutzer ruft im Browser eine URL auf (z.B. https://example.com/foo), der Browser kontaktiert als erstes einen DNS-Server um den Hostnamen example.com in eine IP-Adresse aufzulösen, kontaktiert dann den Webserver mit einem GET request für den entsprechenden Pfad /foo und bekommt ein HTML Dokument zurück. Wird TLS verwendet, gibt es zusätzliche Schritte, die garantieren, dass die Identität des Servers korrekt ist und die Daten bei der übertragung nicht verfälscht wurden, aber sonst gilt das gleiche Prinzip. Dieses HTML Dokument enthält typischerweise bereits darstellbaren Textinhalt, aber für eine visuelle Aufbereitung braucht es noch weitere Resourcen, die im HTML verlinkt sind und den Browser zu weiteren GET requests anstoßen. So treffen nach und nach (oder seit HTTP/2 auch parallel) die relevanten Daten ein, und der Browser adaptiert die Darstellung der Webseite, wobei das heutzutage oft so schnell passiert, dass es sich visuell als sofortiger Prozess darstellt.
Ob die Dokumente tatsächlich statisch vom Server verschickt wurde, oder auf Abruf generiert, ist für den Benutzer erstmal nicht einsehbar, das ist ein Teil der Serverprogrammierung selber, aber am Anfang des Webs waren statische Webseiten noch sehr weit verbreitet. HTTP sah von Anfang an neben GET requests auch POST requests vor, mit denen Daten an den Server geschickt werden können. Dies konnte z.B. für eine Newsletter Anmeldung verwendet werden, die response von dem Server kann immer noch statisch sein (“Danke für Ihre Anmeldung”) aber der Server verarbeitet die Daten intern weiter.
Es wurde schon in den frühen 90ern mit dynamisch generierten Inhalten experimentiert, aber der große Durchbruch kam mit PHP im Jahr 1995. Die einfachsten Anwendungen waren zunächst Besucherzähler oder Gästebücher, die sich oft auf privaten Webseiten fanden. Auf dem Server liegen jetzt PHP-Dokumente, die immer noch alles an statischem HTML Code unterstützen, aber auch erlauben, an bestimmten Stellen die aktuellen Daten aus anderen Quellen, wie ein zusätzliches Dokument oder eine Datenbank, einzubinden, so dass z.B. der Zähler nach jedem Aufruf eine neue Zahl anzeigt. Alternativ kann der Server aber auch generierte HTML-Dokumente cachen, das heißt, erst nach ein paar Minuten wird ein neues Dokument generiert und somit z.B. ein neue Eintrag im Gästebuch angezeigt. Hier fängt auch Nutzerverwaltung an und damit werden ganz neue Bereiche erschlossen wie Foren, Onlineshops oder Webmail, wo die relevanten Informationen privat und erst nach Authentifizierung verfügbar sind.
Auch 1995 wurde JavaScript eingeführt, eine vom Browser ausführbare Skripting-Sprache, die zunächst nur verwendet wurde, um den Funktionsumfang von HTML zu erweitern. Typische Anwendungen waren Validierung von Formulareingaben oder Werbebanner die sich auf Klick oder Hover aktivieren. Im Prinzip kann mit JavaScript die gesamte HTML-Struktur der Webseite beliebig beeinflusst werden, aber damals konnte JavaScript alleine noch keine neuen Requests schicken, das heißt, es konnte nur mit den im ursprünglichen HTML vorhandenen oder verlinkten Resourcen gearbeitet werden.
Dies führte in den 2000ern zu immer mehr nutzerbasierten Webanwendungen bis hin zu den sozialen Medien. Auch die Webentwicklung selber fand immer mehr im Browser statt, mit der Einführung von WordPress 2003 wurde es auch für Privatanwender sehr einfach, eine persönliche Webseite direkt im Admin-Bereich zu verwalten, man musste keine Einträge mehr händisch in die Datenbank schieben oder PHP-Dokumente erstellen. Somit wird der Browser zu einem wichtigen Werkzeug für sowohl Konsum von Webinhalten als auch Gestaltung und kreative Arbeit.
Auch bekommen die Browser jetzt über XMLHttpRequest die Möglichkeit, neue Requests direkt über JavaScript zu versenden. Dies ermöglicht partielle Updates der Webseite ohne einen Link zu klicken oder den Browser zu refreshen. Speziell für soziale Medien oder Webmail war dies relevant, weil die Webseite sich jetzt selber updaten und z.B. neue Posts oder Emails anzeigen kann ohne weitere Interaktion mit dem Nutzer. Ein anderer Anwendungsfall waren Wetter-Widgets oder Börsenticker, die jetzt Minutengenaue Informationen einblenden konnten, und natürlich alle Arten von Werbung, denn Werbetreibende wollen am liebsten selber den Inhalt ausliefern, statt sich auf die vom Webseitenbetreiber angegebenen Klickzahlen zu verlassen.
Diese neue Technologie wurde oft AJAX genannt, Asynchronous JavaScript and XML, weil die abgefragt Daten oft in XML formatiert waren, wobei auch andere Formate wie JSON zum Einsatz kamen. Der vielleicht bekannteste Anwendungsfall für die Technologie war Google Maps, man konnte sich frei auf der Karte bewegen und die neuen Informationen wurden kontinuierlich nachgeladen, ohne dass je eine Seitennavigation stattfindet. Über JavaScript kann der Browser sogar direkt das URL-Fragment (den Teil der URL nach dem #-Symbol) updaten, so dass zu jedem Zeitpunkt der aktuelle Kartenausschnitt in der URL mitkodiert ist, was das versenden oder bookmarken von Kartenmaterial erleichtert. Diese hashes sind kein Teil des HTTP-Requests, sie wurden ursprünglich nur verwendet, um einen bestimmten Abschnitt der Webseite zu adressieren, aber können auch von JavaScript abgefragt werden und waren zu diesem Zeitpunkt eine wichtige Brückentechnologie, weil JavaScript noch nicht vollen Einfluss auf die Browser History nehmen konnte.
Anfang der 2010er Jahre wurde immer mehr mit AJAX und hybriden Ladestrategien experimentiert. Weiter wurden JavaScript Libraries konstruiert, die nicht nur auf einfache Manipulation von HTML Elementen ausgelegt waren, sondern auf das Rendering von HTML selber. Mit der neuen History API konnte zudem JavaScript jetzt nich nur Fragments manipulieren sondern normale Pfade in die Browser history pushen, was dazu führte, dass man fast alles Aspekte einer klassischen Webseite mit verschiedenen Pfaden jetzt komplett in JavaScript abbilden konnte, ohne dass es für den Nutzer gleich ersichtlich war. Ein Klick auf einen Pfad wie /about konnte abgefangen werden, die entsprechenden Daten über JavaScript geladen und gerendert und der neue Pfad in die History gepusht. Dieses Konzept nennt sich SPA (single page application), während das klassische Webseitenkonzept als often als MPA (multi page application) betitelt wurde. Das war zum einen attraktiv für JavaScript Entwickler, weil sie gewisse Backendaufgaben direkt ins Frontend übernehmen konnten, aber lieferte zum Teil auch eine bessere UX (User Experience), weil Nutzergenerierte Zustände, wie z.B. eine ein- oder ausgeblendete Sidebar, jetzt ganz trivial über Navigationsevents hinweg erhalten bleiben.
Gleichzeitig ist der Nachteil an SPAs, dass nach dem Laden der initialen Webseite noch gar keine Inhalte angezeigt werden, das HTML enthält oft nur ein paar meta tags und die Links zu den entsprechenden JavaScript Dokumenten. Diese müssen je nach caching Status geholt und ausgeführt werden, was oft noch weiter requests in Richtung Server anstößt, bevor tatsächlich Inhalte angezeigt werden. Auch für SEO (Search Engine Optimization) war das anfangs noch sehr schlecht, weil die crawler von Google oder ähnlichen Diensten kein JavaScript ausführten, sie besuchten nur die Domains und verarbeiteten die im HTML direkt verfügbaren Informationen. Heutzutage haben die crawler hier mehr Optionen, also hat sich die SEO-Situation für SPAs leicht verbessert.
Mitte der 2010er Jahre nahmen verschiedene technologische Entwicklungen zugleich Fahrt auf. Zum einen wurde JavaScript zunehmend auch auf dem Server eingesetzt, das wichtigste Projekt hierfür war Node.js, das die V8-Engine aus Chromium außerhalb des Browsers nutzbar machte. JavaScript-Entwickler konnten damit nun auch direkt Backend-Anwendungen entwickeln. Gleichzeitig verlor das Backend ein Stück weit seine traditionelle Bindung an HTML. Während PHP ursprünglich gerade darauf ausgelegt war, HTML-Dokumente dynamisch zu erzeugen, boten sich neuere Server-Frameworks mehr dafür an, über APIs strukturierte Daten bereitzustellen.
Parallel dazu wurden Content Delivery Networks, kurz CDNs, zu einem immer wichtigeren Bestandteil der Web-Infrastruktur. Ein CDN besteht aus geografisch verteilten Servern, die Kopien häufig angefragter Ressourcen zwischenspeichern und sie möglichst nahe am Nutzer ausliefern. Das reduzierte nicht nur Ladezeiten, sondern auch die Last auf dem eigentlichen Webserver. Für öffentlich cachebare Inhalte bedeutete das, dass eine populäre Website nicht zwangsläufig einen entsprechend großen Ursprungsserver benötigte: Ein erheblicher Teil des Traffics konnte bereits vom CDN beantwortet werden.
Caching verschob damit die Grenze zwischen statischen und dynamischen Webseiten. Eine Seite konnte weiterhin dynamisch auf einem vergleichsweise kleinen Server generiert werden, aber das resultierende HTML anschließend für Minuten oder Stunden im CDN liegen. Tausende Nutzer konnten dann dieselbe Seite abrufen, ohne dass der eigentliche Anwendungsserver für jeden Request erneut PHP, JavaScript oder eine Datenbankabfrage ausführen musste. Dynamische Generierung und statische Auslieferung waren also keine Gegensätze mehr.
Die zunehmende Trennung von Daten, Darstellung und Auslieferung zeigte sich auch bei Content-Management-Systemen. Systeme wie WordPress hatten traditionell mehrere Aufgaben zugleich übernommen: Sie speicherten Inhalte, stellten eine Oberfläche zu deren Bearbeitung bereit und generierten schließlich die HTML-Seiten für die Besucher. Mit sogenannten Headless-CMS wurde dieser letzte Schritt abgetrennt. Das CMS verwaltet weiterhin die Inhalte, stellt sie aber über eine API bereit; eine davon unabhängige Anwendung entscheidet, wie daraus eine Website oder eine andere Benutzeroberfläche entsteht.
Das brachte architektonische Vorteile, hatte aber auch einen Preis. Bei klassischen CMS waren Bearbeitung und spätere Darstellung eng miteinander verbunden, wodurch WYSIWYG-Editoren den Autoren unmittelbar vermitteln konnten, wie eine Seite aussehen würde. Bei einem Headless-CMS kennt das CMS die endgültige Darstellung dagegen häufig gar nicht mehr. Inhalte werden stärker als strukturierte Daten behandelt. Dadurch wurde die Architektur flexibler und Inhalte konnten leichter von verschiedenen Anwendungen verwendet werden, gleichzeitig ging aber ein Teil der unmittelbaren visuellen Bearbeitung verloren.
Eine konsequente Weiterentwicklung dieser Ideen war das von Netlify popularisierte JAMstack-Modell – ursprünglich ein Akronym für JavaScript, APIs und Markup. Statt HTML bei jedem Request auf einem Anwendungsserver neu zu generieren, wurde die Website bereits beim Deployment erzeugt. Ein sogenannter Static Site Generator konnte dazu beispielsweise Inhalte aus Markdown-Dateien oder über die API eines Headless-CMS beziehen und daraus fertige HTML-Dateien erstellen. Diese wurden anschließend über ein CDN verteilt.
Static Site Generation, kurz SSG, war technisch keine vollkommen neue Idee. HTML-Seiten im Voraus zu generieren war schon wesentlich früher möglich. Neu war vor allem die Integration dieses Prinzips in einen weitgehend automatisierten Entwicklungs- und Deployment-Prozess. Ein Push in ein Git-Repository konnte einen Build auslösen, der die Inhalte aus verschiedenen Quellen abfragte, daraus die Website generierte und das Ergebnis anschließend automatisch weltweit auf CDN-Server verteilte. Ebenso konnte ein Headless-CMS über einen Webhook einen neuen Build anstoßen, sobald ein Redakteur einen Inhalt änderte.
In gewisser Hinsicht verschob SSG damit lediglich den Zeitpunkt des Cachings. Bei einer klassischen dynamischen Website kann der erste Request eine HTML-Seite auf dem Server erzeugen, die anschließend von einem Cache oder CDN gespeichert und für weitere Requests wiederverwendet wird. Bei SSG findet diese Generierung schon vor dem ersten Request statt: Der Build-Prozess erzeugt zentral die fertigen Dokumente und verteilt sie anschließend. Für den Nutzer ist das Ergebnis ähnlich – sein Request kann direkt von einem CDN beantwortet werden, ohne dass dafür der eigentliche Anwendungsserver aktiv werden muss.
Damit ließ sich ein erstaunlich großer Teil einer Website vollständig ohne dauerhaft laufenden Anwendungsserver ausliefern. Dynamische Funktionen mussten deshalb aber nicht verschwinden. JavaScript konnte nach dem Laden weiterhin APIs ansprechen, Formulare abschicken oder personalisierte Daten nachladen. Eine Seite konnte also aus statisch ausgeliefertem HTML bestehen und sich im Browser trotzdem wie eine dynamische Anwendung verhalten.
Gegen Ende der 2010er und Anfang der 2020er Jahre wurde allerdings deutlich, dass weder reine SPAs noch vollständig statisch generierte Websites für jeden Anwendungsfall ideal waren. Bei SSG-Webseiten wird durch jede noch so kleine inhaltliche Änderung ein neuer Build von allen vorhandenen Webseiten angestoßen, für größere Webseiten wie Shops oder Archive kann das lange dauern und teure Rechenzeit beanspruchen.
SSG-Frameworks reagierten darauf mit inkrementellen Verfahren. Statt bei jeder Änderung die gesamte Website neu zu generieren, können nur diejenigen Seiten neu erzeugt werden, die tatsächlich veraltet sind. Dies ist aber auch kein triviales Problem, weil nicht jeder Inhaltsblock im CMS genau einer Unterseite zugeordnet ist, sondern die Transformation nach beliebig komplexen Logiken erfolgen kann.
Gleichzeitig erlebte auch Server-Side Rendering eine neue Form. Frameworks wie Next.js, Remix oder Fresh machten wieder stärker Gebrauch davon, HTML für einen Request auf dem Server zu erzeugen, kombinierten dies aber mit den inzwischen etablierten Techniken des clientseitigen JavaScripts. Eine Anwendung kann dadurch beim ersten Aufruf ein vollständiges HTML-Dokument liefern und sich nach dem Laden des JavaScripts trotzdem wie eine SPA verhalten. Dieser Übergang, bei dem das bereits vorhandene HTML im Browser mit JavaScript interaktiv gemacht wird, wird häufig als Hydration bezeichnet.
Auch das CDN selbst wandelte sich. Ursprünglich bestand seine Aufgabe hauptsächlich darin, bereits erzeugte Dateien oder gecachte Responses möglichst nahe am Nutzer bereitzuhalten. Mit Edge Computing können inzwischen jedoch auch Programme direkt auf der weltweit verteilten Infrastruktur eines CDN-Anbieters ausgeführt werden. Eine Response muss damit weder vollständig vorgeneriert sein noch von einem zentralen Server stammen: Teile einer Seite können erst am geografisch nahegelegenen Edge-Server dynamisch erzeugt werden.
Damit schließt sich in gewisser Weise ein Kreis. Das moderne Web lässt sich immer weniger eindeutig in statische Seiten, serverseitig gerenderte Anwendungen und SPAs einteilen. Dieselbe Anwendung kann statisch vorgenerierte Seiten, gecachte Responses, serverseitiges Rendering, Edge Rendering und clientseitiges Rendering miteinander kombinieren. Die zentrale Frage ist nicht mehr, wo eine Webanwendung gerendert wird, sondern welche Teile zu welchem Zeitpunkt und an welchem Ort gerendert werden.