Die Redaktion lädt ein Foto hoch. Das Frontend fragt nach genau der Variante, die es braucht — 600 Pixel breit, 2x für Retina, als AVIF wenn der Browser es kann. RustyCMS rechnet das beim ersten Aufruf, cached es und liefert es ab dann aus dem Speicher. Kein Cloudinary, kein imgproxy, kein Build-Schritt.
Ein Endpoint, alle Varianten
# 600px breit, Retina, Format nach Accept-Header
GET /api/transform?url=/api/assets/hero.jpg&w=600&dpr=2&format=auto
# Quadrat-Ausschnitt um den Fokuspunkt
GET /api/transform?url=/api/assets/portrait.jpg&w=400&h=400&fx=0.3&fy=0.2
# Fertige <picture>-Quellen für responsive Bilder
GET /api/transform/srcset?url=/api/assets/hero.jpg&widths=400,800,1200&formats=avif,webp,jpeg
Parameter: Breite, Höhe, Seitenverhältnis, fit (fill/contain/cover), Fokuspunkt oder Anker, Format, Qualität, Rotation, Spiegelung, Graustufen. EXIF-Orientierung wird automatisch korrigiert — das Handyfoto steht richtig herum, ohne dass jemand daran denkt.
Format nach Browser, nicht nach Bauchgefühl
format=auto liest den Accept-Header: AVIF, wenn der Browser es kann, sonst WebP, sonst JPEG. Der Cache-Schlüssel bindet an das tatsächlich ausgelieferte Format, Vary: Accept steht im Response — ein Safari-Nutzer bekommt nie das AVIF, das für Chrome gerechnet wurde.
Platzhalter, bevor das Bild da ist
/api/transform/metadata liefert Breite, Höhe, Seitenverhältnis, dominante Farbe und einen Blurhash. Damit reserviert das Frontend den Platz und zeigt eine unscharfe Vorschau, bevor ein einziges Byte des echten Bildes geladen ist. Der Blurhash lässt sich als winziges JPEG abrufen — cache-immutable, ein Request für die ganze Site.
Zweistufiger Cache, einmal rechnen
Erst In-Memory-LRU mit Byte-Budget, dahinter ein Disk-Layer, der einen Neustart überlebt. Fragen zehn Besucher gleichzeitig nach derselben noch nicht gerechneten Variante, dekodiert nur einer — die anderen warten auf sein Ergebnis. Responses tragen ETag und Cache-Control: immutable, ein Reverse Proxy oder CDN davor hält die Last vom Prozess fern.
Was nicht passiert
Externe URLs werden nur verarbeitet, wenn der Host explizit freigeschaltet ist — der Endpoint ist kein offener Proxy. Redirects werden nicht verfolgt, Quellbilder über 64 Megapixel abgelehnt. Die Pipeline gehört dir, nicht dem Internet.