It's all in your <head>

Hallo

Mein Name ist Marco

Mein Name ist Marco

Ich

  • bin verheiratet.
  • bin Vater von zwei Mädels (19 und 12).
  • Meine Pronomen sind er/ihn.

Mein Name ist Marco

Ich

  • bin verheiratet.
  • bin Vater von zwei Mädels (19 und 12).
  • Meine Pronomen sind er/ihn.
  • liebe Musik und Tanzen.
  • lese gern.
  • bin Casual-Gamer und Speedrunner (Super Metroid).
  • werfe gerne Bälle auf Körbe.

Mein Name ist Marco

Ich

  • bin verheiratet.
  • bin Vater von zwei Mädels (19 und 12).
  • Meine Pronomen sind er/ihn.
  • liebe Musik und Tanzen.
  • lese gern.
  • bin Casual-Gamer und Speedrunner (Super Metroid).
  • werfe gerne Bälle auf Körbe.
  • habe mit 6 Jahren meinen ersten Computer gehabt.
  • bin seit 1996 online.
  • bin Front-End-Entwickler.

Smashing Magazine

Januar 2013 – Mitte 2016

Smashing-Magazine-Artikelseite in Desktop- und Mobil-Ansicht: Web Development Reading List #119 von Anselm Hannemann, Januar 2016

Land in Sicht

2016 – 2020

Solothurn Tourismus Website: Panoramablick über die Solothurner Altstadt mit der St. Ursenkathedrale Website des Schwarzwälder Freilichtmuseums Vogtsbauernhof: Frau mit traditionellem Bollenhut vor historischen Schwarzwaldhäusern

Aufwind

seit 2020

Website Diakoniekrankenhaus Freiburg: Pflegekraft und Arzt im Klinikflur, Headline 'Nahe am Menschen' Website Rodi: moderne Hotelrezeption, Headline 'Design trifft Funktion'
Die Pyramiden von Gizeh in der Wüste unter blauem Himmel, im Vordergrund winzige Figuren und ein Pferdewagen als Größenvergleich
Die Basilika Sagrada Família in Barcelona, ihre verzierten Türme vor blauem Himmel, daneben ragt noch ein Baukran auf
Die geflochtene Aluminiumfassade der Messe Basel, geschwungen vor klarem blauem Himmel, mit ihrer zentralen kreisrunden Öffnung
Drei bärtige alte Männer in mittelalterlichem Gewand; einer hält einen Greifvogel auf der behandschuhten Hand, einem anderen sitzt ein Rabe auf der Schulter
Ein Kleinkind mit großen Augen wird aus einer blauen Schüssel gefüttert, Gesicht und Shirt voller violettem Beerenbrei
Screenshot der Apple-Computer-Website aus den 1990ern in einem alten Macintosh-Browser, mit dem Regenbogen-Apple-Logo und Navigation in Pinselstrich-Optik
Screenshot der Space-Jam-Website von 1996: Planeten als Navigations-Links auf schwarzem Sternenhimmel rund um das Space-Jam-Logo
Screenshot der eBay-Startseite aus den späten 1990ern mit dem bunten Logo, einer langen Liste von Kategorie-Links und dichtem Layout aus blauen Links
Screenshot von Taylor Swifts offizieller Website von 2007, ein skeuomorphes Scrapbook-Design voller glänzender Promo-Buttons rund um ein Foto der jungen Taylor Swift
Screenshot von Header und Hero der Apple-Startseite 2025: die globale Navigation über der großen Überschrift „MacBook Air“ und einem schwebenden himmelblauen MacBook

Warum der <head>?

Erste Bytes

Diagramm eines einzelnen Round-Trips: ein Smartphone, ein Funkmast und ein Server mit einem Pfeil, der den Request hin und zurück zeigt

Entwickler lieben Abkürzungen

  • TTFB – Time to First Byte
  • LCP – Largest Contentful Paint
  • FCP – First Contentful Paint
  • CLS – Cumulative Layout Shift

Aber am Ende geht's nur um Render-blocking

Aber am Ende geht's nur um Render-blocking

und wie man's verhindert

Aber am Ende geht's nur um Render-blocking

und wie man's verhindert

indem man seinen <head> richtig aufbaut

Und die Wahrheit ist:

Es ändert sich kaum.

Was sich wirklich geändert hat

Dazugekommen

  • rel="preload"
  • preconnect
  • prefetch

Rausgeflogen

  • X-UA-Compatible
  • meta name="keywords"
  • Der Favicon-Wildwuchs

Die Reihenfolge

Das ist es

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Page Title</title>
    <link rel="preload" href="/css/site.css?v=0.0.1" as="style" type="text/css">
    <link rel="preload" href="/fonts/primary.woff2" as="font" type="font/woff2" crossorigin>
    <script>/* fontloading.js inlined */</script>
    <style>/* critical CSS inlined */</style>
    <link rel="stylesheet" media="print" onload="this.media='screen'" href="/css/site.css?v=0.0.1">
    <noscript><link rel="stylesheet" href="/css/site.css?v=0.0.1"></noscript>
    <meta name="description" content="…">
    <link rel="canonical" href="…">
    <script>/* cut-the-mustard inlined */</script>
    <link rel="icon" type="image/svg+xml" href="/favicon/favicon.svg">
    <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png">
    <link rel="manifest" href="/site.webmanifest">
</head>

Danke für eure Zeit

10 Schritte, immer in dieser Reihenfolge

  1. meta charset
  2. viewport
  3. title

10 Schritte, immer in dieser Reihenfolge

  1. meta charset
  2. viewport
  3. title
  4. Preloads (CSS, Fonts)
  5. Font Loading (inline JS)
  6. Critical CSS (inline)
  7. Async Main Stylesheet

10 Schritte, immer in dieser Reihenfolge

  1. meta charset
  2. viewport
  3. title
  4. Preloads (CSS, Fonts)
  5. Font Loading (inline JS)
  6. Critical CSS (inline)
  7. Async Main Stylesheet
  8. SEO Meta (description, canonical, hreflang)
  9. Feature Detection (inline JS)
  10. Favicons

Warum diese Reihenfolge?

1. meta charset

Charset – als allererstes

<meta charset="utf-8">

2. viewport

Der Standard-Viewport

<meta name="viewport"
      content="width=device-width, initial-scale=1">

Lass den Zoom in Ruhe

<!-- Bitte nicht -->
<meta name="viewport"
      content="width=device-width,
               initial-scale=1,
               maximum-scale=1,
               user-scalable=no">

3. title

Title

<title>My beautiful website</title>

4. Preloads

Was ist preload?

Was preloaden?

<link rel="preload"
      href="/css/site.css?v=0.0.1"
      as="style"
      type="text/css" />

<link rel="preload"
      href="/fonts/FiraSans-Variable.woff2"
      as="font"
      type="font/woff2"
      crossorigin />

Die Fallen

Die Fallen

crossorigin · as

Die Fallen

crossorigin · as

type

Die Fallen

crossorigin · as

type

Nicht übertreiben

5. Font Loading

Schicht 1: Preload

<link rel="preload"
      href="/fonts/FiraSans-Variable.woff2"
      as="font"
      type="font/woff2"
      crossorigin />

<link rel="preload"
      href="/fonts/FiraCode-Variable.woff2"
      as="font"
      type="font/woff2"
      crossorigin />

Schicht 2: sessionStorage-Fontloading-Script

(function () {
    'use strict';
    if (sessionStorage.fontsLoaded) {
        document.documentElement.className += ' fonts-loaded-1 fonts-loaded-2';
        return;
    }
    if ('fonts' in document) {
        document.fonts.load('400 1em FiraSans').then(function () {
            document.documentElement.className += ' fonts-loaded-1';
            document.fonts.load('700 1em FiraCode').then(function () {
                document.documentElement.className += ' fonts-loaded-2';
                sessionStorage.fontsLoaded = true;
            });
        });
    }
})();

Schicht 3: font-family im critical CSS

@font-face {
    font-family: 'FiraSans';
    src: url('/fonts/FiraSans-Variable.woff2') format('woff2');
    font-weight: 100 900;
    font-display: swap;
}

body {
    font-family: 'Helvetica Neue', sans-serif;
}

.fonts-loaded-1 body {
    font-family: 'FiraSans', sans-serif;
}

6. Critical CSS

Was ist Critical CSS?

<style>
    /* critical.css */
</style>

Was kommt rein?

@import 'foundation/fonts';
@import 'foundation/normalize';
@import 'foundation/typography';
@import 'foundation/header';
@import 'foundation/hero';

Klein und einfach halten

Idealerweise passen HTML und Critical CSS bis zu diesem Punkt in 14kb.

7. Async Main Stylesheet

Das Hauptstylesheet – ohne zu blocken

<link rel="stylesheet"
      media="print"
      onload="this.media='screen'"
      href="/css/site.css?v=0.0.1" />

<noscript>
    <link rel="stylesheet"
          href="/css/site.css?v=0.0.1" />
</noscript>

Cache-Busting

<link href="/css/site.css?v=0.0.1" />

8. SEO Meta

Description, Canonical, Hreflang

<meta name="description"
      content="Personal website and blog" />

<link rel="canonical"
      href="https://your-website.com/" />

<link rel="alternate"
      hreflang="de"
      href="https://your-website.com/" />
<link rel="alternate"
      hreflang="en"
      href="https://your-website.com/en/" />

9. Feature Detection

Ein cooler One-Liner

<html lang="de" class="no-js">
document.documentElement.classList.replace('no-js', 'js');

Was bringt's?

/* Verstecke JS-only Komponenten, wenn kein JS */
.no-js .js-only {
    display: none;
}

/* Verstecke No-JS-Fallbacks, wenn JS verfügbar */
.js .no-js-only {
    display: none;
}

Warum inline und so früh?

  • Das Script läuft synchron, bevor irgendein CSS rendert.
  • Die Klasse ist am <html>, bevor der erste Pixel gezeichnet wird.
  • Kein zusätzlicher Request, kein defer, keine Race-Condition.

10. Favicons

Das Mindeste

<link rel="icon"
      type="image/svg+xml"
      href="/favicon/favicon.svg">
<link rel="icon"
      type="image/png"
      sizes="32x32"
      href="/favicon/favicon-32x32.png">
<link rel="apple-touch-icon"
      sizes="180x180"
      href="/favicon/apple-touch-icon.png">
<link rel="manifest"
      href="/site.webmanifest">

Warum ganz am Ende?

Der ganze Block

Von oben nach unten

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Page Title</title>
    <link rel="preload" href="/css/site.css?v=0.0.1" as="style" type="text/css">
    <link rel="preload" href="/fonts/primary.woff2" as="font" type="font/woff2" crossorigin>
    <script>/* fontloading.js inlined */</script>
    <style>/* critical CSS inlined */</style>
    <link rel="stylesheet" media="print" onload="this.media='screen'" href="/css/site.css?v=0.0.1">
    <noscript><link rel="stylesheet" href="/css/site.css?v=0.0.1"></noscript>
    <meta name="description" content="…">
    <link rel="canonical" href="…">
    <script>/* cut-the-mustard inlined */</script>
    <link rel="icon" type="image/svg+xml" href="/favicon/favicon.svg">
    <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png">
    <link rel="manifest" href="/site.webmanifest">
</head>

Dieselbe Seite. Zwei Heads.

Filmstreifen-Vergleich derselben Seite, die im Slow-3G-Netz mit zwei verschiedenen Heads lädt. Der naive Head bleibt bis etwa 4,2 Sekunden leer und zeichnet dann; der optimierte Head ist nach etwa 0,6 Sekunden lesbar.

Checklist

Performance-Checklist

  • Korrekt preloaden mit type, as – und crossorigin, wenn es eine Font ist
  • Nicht alles preloaden
  • font-display: swap in @font-face
  • Font-Loading mit Klassen-Toggle und sessionStorage
  • Critical CSS inline
  • Hauptstylesheet async (print-media-Trick)
  • <noscript>-Fallback für Stylesheets
  • Cache-Busting per Version-Query-String
  • Kleine Scripts inline
  • no-js-Klasse am <html>

Performance-Checklist

  • Korrekt preloaden mit type, as – und crossorigin, wenn es eine Font ist
  • Nicht alles preloaden
  • font-display: swap in @font-face
  • Font-Loading mit Klassen-Toggle und sessionStorage
  • Critical CSS inline
  • Hauptstylesheet async (print-media-Trick)
  • <noscript>-Fallback für Stylesheets
  • Cache-Busting per Version-Query-String
  • Kleine Scripts inline
  • no-js-Klasse am <html>
  • Ressourcen selbst hosten
Eine junge Frau mit Brille lächelt, während sie an einem Tisch voller Pinsel und Farbtöpfe malt, konzentriert und ihrem Handwerk gewachsen

Danke!

Fragen?

marco-hengstenberg.de

Pinkes Graffiti: „Ich rebelliere gegen die Zustände“ Fuck AfD Fuck Nazis