Assets mit Brotli Level 11 — durchs CDN

Stand: Juli 2026

Der Bildformat-Fall hat das Muster gezeigt: Das CDN verhandelt pro Browser die beste Variante, ohne dass sich URLs ändern oder das Quellsystem angefasst wird. Dasselbe Content-Negotiation-Prinzip funktioniert eine Schicht tiefer — nicht für Bildformate über den Accept-Header, sondern für Kompression über Accept-Encoding: CSS und JavaScript als Brotli in der höchsten Stufe ausliefern.

Warum sich das lohnt, in Zahlen (gemessen an einer echten Shopware-6-Theme-CSS, 366 KB):

  • gzip −9: 56,5 KB
  • Brotli on-the-fly (Stufe ~5, was Edge oder Origin zur Laufzeit schaffen): 51,8 KB
  • Brotli −q 11, vorkomprimiert: 43,4 KB — 23 % kleiner als gzip, rund 15 % kleiner als das On-the-fly-Brotli

Der springende Punkt: Level 11 ist zur Laufzeit unbezahlbar — die Kompression dauert je Datei Sekunden, das macht kein Server pro Request und kein CDN am Edge. CDNs komprimieren on-the-fly auf mittlerem Niveau, und genau deshalb verschenkt die Standard-Einstellung „Compress objects automatically“ die letzten 15 %.

Warum hier Vorkompression richtig ist (und bei Bildern falsch war)

Bei den Bildern verbot sich die Vorab-Konvertierung: sechsstellige Bestände, laufende Importe, mehrere Zielformate. Assets sind das exakte Gegenteil: ein paar hundert Dateien, die sich ausschließlich beim Deployment ändern. Einmal komprimieren pro Deploy, fertig — der teuerste Kompressionslevel kostet dann genau nichts zur Laufzeit.

# Deploy-Hook: beste Kompression, einmalig pro Release
find public/theme -type f \( -name '*.css' -o -name '*.js' \) \
  -exec brotli -q 11 -kf {} \; \
  -exec gzip -9 -kf {} \;

Die Kette

1. Origin liefert die fertigen Varianten — nginx mit ngx_brotli:

brotli_static on;   # nimmt die .br-Datei, wenn der Browser br kann
gzip_static   on;   # sonst die .gz-Datei
# kein brotli on / gzip on nötig — es wird nichts zur Laufzeit gerechnet

2. Das CDN cached die Varianten getrennt. Bei CloudFront heißt das: eine Cache-Policy, die gzip- und Brotli-Support aktiviert (die Standard-Policy CachingOptimized tut das) — damit wandert das normalisierte Accept-Encoding in den Cache-Schlüssel und jeder Browser bekommt am Edge seine Variante. Das automatische Edge-Komprimieren bleibt aus; das CDN reicht die Origin-Varianten durch.

3. Der Browser merkt nichts — gleiche URL, Content-Encoding: br, nur eben die q-11-Fassung.

Das Ergebnis auf der render-kritischen Theme-CSS ist unmittelbar LCP-wirksam: weniger Bytes auf dem kritischen Pfad, bei null Laufzeit-CPU und null Risiko — schlimmstenfalls fehlt eine .br-Datei und der Browser bekommt gzip, wie bisher.

Die Checkliste

  • Deploy-Hook mit brotli -q 11 + gzip -9 (atomar mit dem Release ausrollen — Fingerprint-URLs machen das trivial: neue Datei, neue URL)
  • Origin: brotli_static + gzip_static aktiv, Laufzeit-Kompression für Assets aus
  • CDN-Cache-Policy: gzip und Brotli als Varianten aktiviert, automatische Edge-Kompression für diese Pfade aus
  • Kontrolle per curl: curl -sI -H 'Accept-Encoding: br' <asset-url> | grep -iE 'content-encoding|content-length' — die Länge muss zur .br-Datei passen, nicht zur On-the-fly-Größe
  • Für das gecachte HTML (das nicht statisch ist) gilt weiter die Varnish-Kette — die beiden ergänzen sich