Hochaufgelöste Fotos sind für Web-Galerien, Produktlisten oder Avatare meist viel zu groß. Mit der GD-Bibliothek lässt sich in PHP aber mit wenigen Zeilen ein passendes Thumbnail erzeugen. Dieser Praxisleitfaden zeigt, welche GD-Funktion sich wofür eignet, wie das Seitenverhältnis erhalten bleibt und wie sich Memory-Probleme und falsch gedrehte Hochformat-Fotos vermeiden lassen.
Wann sollte ich ein PHP Bild verkleinern?
Wer in PHP ein PHP Bild verkleinern möchte, hat fast immer einen praktischen Grund. Originalbilder von modernen Smartphones sind 12 Megapixel oder mehr und damit für Web-Galerien, Produktlisten oder Avatar-Anzeigen viel zu groß. Mit der GD-Bibliothek lässt sich aus jeder JPG-, PNG-, GIF- oder WEBP-Datei in wenigen Zeilen ein passendes Thumbnail erzeugen, ohne externe Software wie ImageMagick installieren zu müssen.

Bevor die Resize-Funktion mit Seitenverhältnis und WEBP-Output entsteht, lohnt der direkte Vergleich der drei GD-Funktionen, die für das Verkleinern in Frage kommen.
Drei Funktionen kommen dafür in Frage: das alte imagecopyresized(), das saubere imagecopyresampled() und der bequeme Wrapper imagescale(). Welche Variante wann passt, wie das Seitenverhältnis erhalten bleibt und wie man typische Memory-Probleme vermeidet, klärt dieses Tutorial Schritt für Schritt.
Welche GD-Funktion eignet sich am besten?
Wer ein PHP Bild verkleinern will, hat in der GD-Bibliothek drei Werkzeuge zur Auswahl, die sich in Qualität und Geschwindigkeit unterscheiden.
Bevor wir an Code gehen, lohnt der Blick auf die drei Kandidaten für das PHP Bild verkleinern. Die alte Funktion imagecopyresized() ist schnell, kennt aber kein Anti-Aliasing und liefert pixelige Ergebnisse. imagecopyresampled() interpoliert die Pixel weicher und ist seit Jahren die Standardwahl für Web-Thumbnails. imagescale() (verfügbar seit PHP 5.5) bietet die kürzeste Schreibweise und entspricht im Ergebnis ungefähr imagecopyresampled().
Für den Alltag gilt: wer ein einzelnes Bild verkleinern möchte und nicht jeden Pixel kontrollieren muss, nimmt imagescale(). Wer Präzisionsarbeit braucht, etwa beim Cropping mit verschobenem Quellausschnitt, greift zu imagecopyresampled(). imagecopyresized() lohnt sich nur in absoluten Performance-Engpässen und wenn die Bildqualität keine Rolle spielt.
Originalgröße mit getimagesize ermitteln
Bevor ein Bild geladen wird, sollte man die Maße kennen. Die Funktion getimagesize() liest Breite, Höhe und Bildtyp aus der Datei, ohne sie komplett in den Speicher zu laden. Das ist auch die sicherste Methode, um den echten Dateityp festzustellen, denn die Endung .jpg allein sagt noch nichts über den tatsächlichen Inhalt.
<?php
$info = getimagesize(__DIR__ . '/foto.jpg');
echo 'Breite: ' . $info[0] . "\n";
echo 'Hoehe: ' . $info[1] . "\n";
echo 'Typ: ' . $info[2] . "\n";
/* IMAGETYPE_JPEG = 2, IMAGETYPE_PNG = 3, IMAGETYPE_WEBP = 18 */
Der dritte Wert im Array ist eine Konstante aus der IMAGETYPE_*-Familie. Sie verrät, welche Lade-Funktion gleich gebraucht wird. Mit dieser Information kann der Resize-Code generisch für JPG, PNG und WEBP funktionieren, ohne dass die Anwendung jedes Format einzeln behandeln muss.
Seitenverhältnis erhalten und neue Maße berechnen
Beim PHP Bild verkleinern gehört das Seitenverhältnis zur Pflichtübung. Eine starre Vorgabe wie "200 mal 200 Pixel" verzerrt rechteckige Fotos zu Quadraten und sieht unprofessionell aus. Die saubere Lösung ist eine "max. Box": Das Bild wird so verkleinert, dass es vollständig in eine vorgegebene Maximalgröße passt, mit unverändertem Verhältnis.
<?php
$origBreite = 4000;
$origHoehe = 3000;
$maxBreite = 800;
$maxHoehe = 600;
/* kleinerer Faktor gewinnt, max 1.0 verhindert Vergroesserung */
$faktor = min($maxBreite / $origBreite, $maxHoehe / $origHoehe, 1.0);
$neuBreite = (int) ($origBreite * $faktor);
$neuHoehe = (int) ($origHoehe * $faktor);
echo $neuBreite . ' x ' . $neuHoehe;
/* 800 x 600 */
Der Trick ist min() mit drei Werten. Die ersten beiden Faktoren beziehen sich auf Breite und Höhe, der dritte Wert 1.0 verhindert, dass ein kleines Original ungewollt vergrößert wird, falls die Zielbox größer ausfällt. So entsteht aus einem Querformat-Foto ein passendes Querformat-Thumbnail, ohne dass das Bild künstlich gestreckt wird.
In Worten formuliert macht die Funktion Folgendes: Falls beide Originalmaße bereits in die Box passen, bleibt alles unverändert. Sprengt eine der Achsen die Box, gewinnt der kleinere Faktor. Ansonsten greift der Standard 1.0. Die min()-Variante kapselt genau dieses Verhalten in einer Zeile.
Eine wiederverwendbare Resize-Funktion bauen
Nachdem die Bausteine geklärt sind, lohnt sich eine generische Funktion, die Quelle, Ziel und Maximalmaße als Parameter nimmt. Sie lädt das Original, berechnet die neuen Maße, ruft imagecopyresampled() auf und speichert das Ergebnis.
<?php
function bild_verkleinern(string $quelle, string $ziel, int $maxBreite, int $maxHoehe): bool
{
$info = getimagesize($quelle);
if ($info === false) return false;
[$origBreite, $origHoehe, $typ] = $info;
$faktor = min($maxBreite / $origBreite, $maxHoehe / $origHoehe, 1.0);
$neuBreite = (int) ($origBreite * $faktor);
$neuHoehe = (int) ($origHoehe * $faktor);
$original = match ($typ) {
IMAGETYPE_JPEG => imagecreatefromjpeg($quelle),
IMAGETYPE_PNG => imagecreatefrompng($quelle),
IMAGETYPE_GIF => imagecreatefromgif($quelle),
IMAGETYPE_WEBP => imagecreatefromwebp($quelle),
default => null,
};
if ($original === null) return false;
$thumb = imagecreatetruecolor($neuBreite, $neuHoehe);
imagecopyresampled($thumb, $original, 0, 0, 0, 0,
$neuBreite, $neuHoehe, $origBreite, $origHoehe);
$ok = imagejpeg($thumb, $ziel, 85);
imagedestroy($original);
imagedestroy($thumb);
return $ok;
}
Die Parameter von imagecopyresampled() wirken auf den ersten Blick erschlagend, folgen aber einer Logik. Erst kommen Ziel- und Quelle-Bild, dann jeweils x- und y-Koordinate für den Start im Ziel und im Original, anschließend Breite und Höhe im Ziel und im Original. So lassen sich auch Crop-Operationen umsetzen, indem die Quelle-Koordinaten und Quelle-Maße einen Bildausschnitt beschreiben.
imagescale als kurzer Shortcut
Wer keinen Crop braucht und nur eine Maximalbreite vorgibt, kommt beim PHP Bild verkleinern mit imagescale() deutlich kürzer durch. Die Funktion akzeptiert ein bereits geladenes GD-Bild und die gewünschte Zielbreite. Bei einer Höhe von -1 wird das Seitenverhältnis automatisch beibehalten.
<?php
$original = imagecreatefromjpeg(__DIR__ . '/foto.jpg');
$thumb = imagescale($original, 400, -1, IMG_BICUBIC);
imagejpeg($thumb, __DIR__ . '/foto_thumb.jpg', 85);
imagedestroy($original);
imagedestroy($thumb);
Der vierte Parameter steuert den Interpolations-Algorithmus. IMG_BICUBIC liefert weiche Kanten und gute Qualität, IMG_NEAREST_NEIGHBOUR ist schneller, aber pixelig. Für Web-Thumbnails ist IMG_BICUBIC praktisch immer die richtige Wahl, weil der Performance-Unterschied bei einzelnen Bildern kaum spürbar ist und das Ergebnis sichtbar besser aussieht.
WEBP-Output für kleinere Dateien
Wer beim PHP Bild verkleinern Bandbreite und Ladezeit reduzieren möchte, sollte das Ergebnis gleich als WEBP speichern. Bei vergleichbarer Qualität liegt die Dateigröße oft 25 bis 35 Prozent unter JPG. Die Funktion imagewebp() arbeitet wie imagejpeg(), akzeptiert aber zusätzlich einen WEBP-spezifischen Qualitätswert.
<?php
$original = imagecreatefromjpeg(__DIR__ . '/foto.jpg');
$thumb = imagescale($original, 600);
imagewebp($thumb, __DIR__ . '/foto.webp', 80);
imagedestroy($original);
imagedestroy($thumb);
/* Qualitaet 80 ist guter Kompromiss aus Groesse und Optik */
Damit das Ergebnis im Browser ankommt, muss der Web-Server beim Ausliefern auch den richtigen Content-Type senden. Statische .webp-Dateien erkennt Apache und Nginx in der Regel automatisch. Wer das Bild dynamisch streamt, setzt vor dem Aufruf einen passenden HTTP-Header mit header(), bspw. header('Content-Type: image/webp').
PNG-Transparenz erhalten mit imagealphablending und imagesavealpha
Wer ein PNG mit transparentem Hintergrund verkleinert, etwa ein Logo oder ein Icon, erlebt eine unangenehme Überraschung: Das Thumbnail bekommt einen schwarzen Hintergrund. Der Grund ist, dass imagecreatetruecolor() standardmäßig mit deckendem Schwarz vorinitialisiert und der Alpha-Kanal beim Speichern ignoriert wird. Zwei zusätzliche Funktionsaufrufe lösen das Problem.
<?php
$original = imagecreatefrompng(__DIR__ . '/logo.png');
[$breite, $hoehe] = [imagesx($original), imagesy($original)];
$thumb = imagecreatetruecolor(200, 100);
/* Pflicht-Setup fuer Alpha-Erhaltung */
imagealphablending($thumb, false);
imagesavealpha($thumb, true);
$transparent = imagecolorallocatealpha($thumb, 0, 0, 0, 127);
imagefilledrectangle($thumb, 0, 0, 200, 100, $transparent);
imagecopyresampled($thumb, $original, 0, 0, 0, 0, 200, 100, $breite, $hoehe);
imagepng($thumb, __DIR__ . '/logo_thumb.png');
imagedestroy($original);
imagedestroy($thumb);
Die Reihenfolge ist wichtig: imagealphablending(false) schaltet das Alpha-Blending vor dem Füllen aus, sodass die Pixel ihre Alpha-Werte behalten. imagesavealpha(true) sorgt dafür, dass imagepng() den Alpha-Kanal in die Datei schreibt. Der transparente Füllrahmen mit imagecolorallocatealpha($bild, 0, 0, 0, 127) initialisiert den Hintergrund ausdrücklich als vollständig durchsichtig (Alpha-Wert 127 ist in GD voll transparent, 0 ist opak). Dasselbe Pattern funktioniert auch für GIF mit transparenter Hintergrundfarbe, wobei dort imagecolortransparent() an Stelle des Alpha-Kanals tritt.
EXIF-Orientation: warum Hochformat-Fotos manchmal gedreht sind
Smartphones speichern Hochformat-Fotos häufig physisch als Querformat plus einem EXIF-Tag, das die korrekte Drehung beschreibt. GD ignoriert diesen Tag beim Laden und erzeugt deshalb Thumbnails, die auf der Seite liegen. Eine kurze Prüfung mit exif_read_data() und imagerotate() löst das Problem.
<?php
$datei = __DIR__ . '/iphone-foto.jpg';
$bild = imagecreatefromjpeg($datei);
$exif = @exif_read_data($datei);
if (!empty($exif['Orientation'])) {
$bild = match ((int) $exif['Orientation']) {
3 => imagerotate($bild, 180, 0),
6 => imagerotate($bild, -90, 0),
8 => imagerotate($bild, 90, 0),
default => $bild,
};
}
imagejpeg($bild, __DIR__ . '/foto_korrigiert.jpg', 90);
imagedestroy($bild);
Die Orientation-Werte 3, 6 und 8 decken die häufigsten Drehungen ab. Mit dem match-Ausdruck bleibt der Code übersichtlich, und das Resize-Verfahren kann anschließend wie gewohnt arbeiten. Wer mit User-Uploads arbeitet, sollte die Korrektur immer am Anfang der Pipeline einfügen, bevor andere Operationen wie Wasserzeichen oder Beschneidung folgen.
Zum Abschluss fasst das folgende Diagramm die komplette Resize-Pipeline zusammen, vom Auslesen der Originalmaße bis zur Ausgabe als JPG oder WEBP.
flowchart TD
A[Originalbild auf Festplatte] --> B[getimagesize liefert Typ und Masse]
B --> C[Faktor und neue Masse berechnen]
C --> D[imagecreatefromjpeg / png / webp]
D --> E[imagecreatetruecolor + imagecopyresampled]
E --> F{Output-Format?}
F -->|JPG| G[imagejpeg mit Qualitaet 85]
F -->|WEBP| H[imagewebp mit Qualitaet 80]
G --> I[imagedestroy]
H --> I
Memory-Limit und typische Stolperfallen
Ein 4000 mal 3000 Pixel großes Originalbild belegt beim PHP Bild verkleinern in imagecreatetruecolor() ungefähr 48 Megabyte Speicher, weil jedes Pixel vier Byte für Rot, Grün, Blau und Alpha belegt. Auf Servern mit knappem memory_limit knallt das Skript schnell mit der Meldung "Allowed memory size exhausted". Eine kurze Berechnung vorab kann das verhindern.
Praktisch bewährt sich, vor dem Laden mit getimagesize() die Maße zu prüfen und bei zu großen Originalen abzubrechen oder über ini_set('memory_limit', '256M') mehr Speicher zu reservieren. Für User-Uploads im Foto-Bereich ist eine Prozesssperre auf maximal 8000 Pixel Kantenlänge ein guter Schutz vor Memory-Exhaust-Angriffen. Wer regelmäßig mit großen Mengen Bildern arbeitet, sollte das Resize entweder per Worker-Prozess auslagern oder pro Datei einen eigenen CLI-Prozess starten, damit der PHP-Prozess danach automatisch alle Ressourcen freigibt.
Fazit zum PHP Bild verkleinern
Das PHP Bild verkleinern mit GD ist mit den richtigen Funktionen ein einfacher Vorgang. imagecopyresampled() liefert Top-Qualität, imagescale() ist die kompakte Variante für den Standardfall, und imagecopyresized() bleibt nur für Spezialfälle. Wichtig sind drei Punkte: das Seitenverhältnis über den kleineren Faktor erhalten, den Memory-Verbrauch im Auge behalten und EXIF-Orientation bei Smartphone-Fotos berücksichtigen. Mit der vorgestellten generischen Resize-Funktion lassen sich JPG, PNG und WEBP einheitlich verarbeiten, und mit imagewebp() als Output entstehen sichtbar kleinere Dateien für schnellere Ladezeiten im Web.