3,9 MB für ein daumengroßes Bild: die Lücke in der Auswahlliste
Ein Vorschaubild zog 3,9 MB, andere Bilder blieben auf dem Handy ganz leer. Beides ging auf dieselbe Ursache zurück: eine Lücke in der Auswahlliste — und einen AVIF-Torso von 145 Byte.
Auf der Website eines Fotografen meldete sich der Kunde mit einem Bildschirmfoto vom Handy: In einer Galerie war statt eines Fotos ein leerer Kasten mit einem Fragezeichen zu sehen. Nicht überall, aber bei mehreren Bildern. Am Rechner war alles in Ordnung.
Solche Meldungen klingen zunächst nach einem Ladefehler oder einer schlechten Mobilfunkverbindung. Die Ursache lag tiefer, und sie erklärt zugleich, warum Fotoseiten auf Telefonen oft unnötig viele Daten übertragen.
Die Diagnose
Ein Bild im Web wird nicht als eine Datei ausgeliefert, sondern als Liste von Fassungen. Der Browser sucht sich daraus die passende. Wie breit das Bild tatsächlich dargestellt wird, weiß der Server aber nicht — er schreibt eine Schätzung ins sizes-Attribut, und der Browser entscheidet danach.
Der Blick in den Quelltext der Seite zeigte die Liste eines betroffenen Bildes:
300w, 520w, 768w, 940w, 1024w, 8640w
Zwischen 1024 und 8640 lag nichts. Der Grund: Auf dieser Installation waren die üblichen Zwischenstufen von WordPress abgeschaltet, und die Vorlage kam direkt aus einer 50-Megapixel-Kamera.
Damit ist der Fehler erklärt. Ein Telefon mit dreifacher Pixeldichte stellt das Bild 390 Pixel breit dar und braucht dafür rund 1240 echte Pixel. Der größte Kandidat darunter liegt bei 1024 — zu wenig. Also nimmt der Browser regelgerecht den nächstgrößeren, und das ist die unskalierte Vorlage. Am Rechner ohne hohe Pixeldichte greift derselbe Browser zum 520er und merkt nichts.
Nachgemessen über sieben Beiträge: 390 Bilder, davon 334 mit einem Kandidaten oberhalb von 2048 Pixeln. Ein einzelnes Vorschaubild zog 3,9 MB.
Blieb die Frage, warum manche Bilder gar nicht erschienen. Die Antwort stand in der Dateigröße:
…_11.avif 145 Bytes
…_11.webp 3.977.666 Bytes
…_11.jpg 1.792.553 Bytes
Die AVIF-Fassung der Vorlage war 145 Byte groß. Der Kodierer war an den 50 Megapixeln gescheitert und hatte eine leere Hülle hinterlassen — eine Datei mit Dateikopf und ohne Bild. Das Plugin hatte nur geprüft, ob die Datei existiert, und sie brav ausgeliefert. Am Rechner fiel auch das nicht auf, weil dort eine kleinere, heile Fassung gewählt wurde.
Die Lösung
Drei Eingriffe, die zusammengehören.
1. Die Auswahlliste deckeln. Die unskalierte Vorlage gehört nicht ins Raster. Sie bleibt der Großansicht vorbehalten, wo der Besucher ausdrücklich Detail sehen will:
// Erst deckeln, dann übersetzen: so taucht das Original weder als
// JPEG- noch als AVIF- oder WebP-Kandidat im Raster auf.
$srcset = self::grid_srcset( $img['srcset'], (int) BBG_Settings::get( 'grid_max_width', 2048 ) );
2. Prüfen statt vertrauen. Vorhandensein ist kein Beleg für Brauchbarkeit. Die Kernfunktion wp_get_image_mime() liest die Kennung am Dateianfang und beantwortet genau diese Frage:
$bytes = @filesize( $target );
if ( false === $bytes || $bytes < BBG_Image::MIN_VARIANT_BYTES ) {
return false;
}
// Das Format aus der Datei selbst bestimmen, nicht aus der Endung.
$mime = wp_get_image_mime( $target );
return 'avif' === $format ? 'image/avif' === $mime : 'image/webp' === $mime;
Scheitert die Prüfung, wird die Datei gelöscht statt behalten. Das ist wichtig, weil ein zurückgelassener Torso sonst bei jedem weiteren Lauf als erledigte Arbeit gilt und nie neu erzeugt wird.
3. Die fehlende Zwischenstufe nachrüsten. Der Deckel allein macht das Raster heil, lässt als größte Fassung aber 1024 übrig. Erst eine echte 2048er schließt die Lücke. Bemerkenswert dabei: Das Verkleinern der 50-Megapixel-Vorlage gelang problemlos — WordPress hatte daraus ja auch die Fassungen bis 1024 erzeugt. Gescheitert war allein das AVIF-Kodieren in voller Auflösung.
Was daraus geworden ist
Aus der Korrektur ist ein eigenes Galerie-Plugin geworden, BitBlade Gallery. Es misst nach dem Aufbau der Seite die tatsächliche Breite jedes Bildplatzes und trägt sie ins sizes-Attribut nach, statt es bei der Schätzung zu belassen. Hoch- und Querformat stehen in gleich hohen Zeilen nebeneinander, ohne Beschnitt. Die Großansicht zoomt bis in die unskalierte Vorlage.
Das Plugin ist beim WordPress-Verzeichnis eingereicht. Der Quellcode liegt diesem Beitrag bei (ZIP, 182 KB, GPL-2.0-or-later).
Ein Detail aus der Einreichung, das Zeit kosten kann: Plugin URI und Author URI dürfen im Plugin-Kopf nicht dieselbe Adresse tragen. Beide Angaben sind freiwillig, gleich sein dürfen sie nicht. Die Einreichung wird sonst automatisch abgewiesen, bevor ein Mensch sie ansieht.
Was man daraus mitnehmen kann
- Ein Bild ist eine Liste, keine Datei. Wer Bildgrößen abschaltet, verändert diese Liste. Lücken darin führen dazu, dass der Browser nach oben ausweicht.
- Hohe Pixeldichte verdreifacht den Bedarf. Was am Rechner unauffällig ist, kann auf dem Telefon die dreifache Auflösung anfordern — und damit eine ganz andere Datei.
- Fehler, die nur auf einem Gerätetyp auftreten, sind fast immer Auswahlfehler. Die Datei ist in Ordnung, die Angabe über sie nicht.
file_exists()ist keine Qualitätsprüfung. Bildbibliotheken melden bei sehr großen Vorlagen Erfolg und schreiben trotzdem eine unbrauchbare Datei. Mindestgröße und Dateikennung prüfen.- Kodierer haben Grenzen, Verkleinerer kaum. Dieselbe Vorlage, an der die AVIF-Kodierung scheiterte, ließ sich ohne Weiteres auf 2048 Pixel verkleinern.
- Im Zweifel nachmessen, nicht annehmen. Sichtbar wurde der Fehler erst, als wir die tatsächlich ausgelieferten Dateigrößen abgefragt haben statt den Quelltext zu lesen.