Um die Ladezeit für Websites zu verkürzen, lohnt es sich über Optimierung der eingesetzten Bilder nachzudenken. Es gibt sehr viele Werkzeuge für die Optimierung der Bilder direkt auf dem Linux-Server.
JPEG Optimierung mit Google Guetzli
Google Guetzli ist ein JPEG-Encoder, der darauf ausgelegt ist, Bilder mit hoher visueller Qualität und gleichzeitig geringerer Dateigröße zu erzeugen.
Installation unter Ubuntu
sudo apt update && sudo apt install guetzli -y
Script zur Nutzung
Folgendes Script durchsucht ein Verzeichnis rekursiv nach JPEG Bildern und optimiert diese.
#!/bin/bash
set -euo pipefail
if [ $# -eq 0 ]; then
echo "Error: no directory given"
exit 1
fi
DIR=$1
if [ ! -d "$DIR" ]; then
echo "Error: directory '$DIR' does not exist"
exit 2
fi
REALDIR=$(realpath "$DIR")
echo "Optimizing directory: $REALDIR"
optimize_with_guetzli() {
local input="$1"
local output="$2"
if guetzli --quality 95 --nomemlimit "$input" "$output" >/dev/null 2>&1; then
mv "$output" "$input"
return 0
else
rm -f "$output"
return 1
fi
}
export -f optimize_with_guetzli
find "$REALDIR" -type f -name "*.jpg" -print0 | while IFS= read -r -d '' IMAGE; do
echo "Optimizing: $IMAGE"
echo "Before: $(du -h "$IMAGE" | awk '{print $1}')"
if optimize_with_guetzli "$IMAGE" "$IMAGE.new"; then
echo " ... Success (Guetzli)"
else
echo " ... Guetzli failed, retrying with ImageMagick"
TMPPNG="$IMAGE.png"
if convert "$IMAGE" "$TMPPNG"; then
if optimize_with_guetzli "$TMPPNG" "$IMAGE.new"; then
rm "$TMPPNG"
echo " ... Success (ImageMagick + Guetzli)"
else
echo " ... Error: Guetzli failed again on $IMAGE"
rm -f "$TMPPNG"
fi
else
echo " ... Error: ImageMagick conversion failed for $IMAGE"
fi
fi
echo "After: $(du -h "$IMAGE" | awk '{print $1}')"
done
PNG Optimierung mit OptiPNG
Installation unter Ubuntu
sudo apt update && sudo apt install optipng -y
Script zur Nutzung
Das Script durchsucht einen angegebenen Pfad rekursiv und optimiert alle gefundenen PNG-Bilder bis keine Optimierung mehr möglich ist.
#!/bin/bash
# png-optimize-lossless.sh
# Optimiert PNGs rekursiv nur mit optipng (verlustfrei), bis keine Reduktion mehr möglich ist.
# Nutzung: ./png-optimize-lossless.sh /pfad/zum/verzeichnis
#
# Optional:
# BACKUP_ORIGINALS=1 -> behält .bak-Kopien der Originale
# STRIP_META=1 -> entfernt Metadaten (empfohlen)
set -u -o pipefail
TARGET_DIR="${1:-}"
if [[ -z "$TARGET_DIR" || ! -d "$TARGET_DIR" ]]; then
echo "Fehler: Bitte existierenden Zielpfad angeben."
echo "Nutzung: $0 /pfad/zum/verzeichnis"
exit 1
fi
BACKUP_ORIGINALS="${BACKUP_ORIGINALS:-0}"
STRIP_META="${STRIP_META:-1}"
total_bytes() {
find "$TARGET_DIR" -type f -iname '*.png' -print0 \
| xargs -0 stat -c '%s' 2>/dev/null | awk '{s+=$1} END{print (s==""?0:s)}'
}
human() {
local b=${1:-0}
awk -v b="$b" 'function h(x){s="B KB MB GB";split(s,a," ");i=1;while(x>=1024&&i<length(a)){x/=1024;i++}return sprintf("%.2f %s",x,a[i])}BEGIN{print h(b)}'
}
make_tmp() { mktemp "${TMPDIR:-/tmp}/optipng.XXXXXXXX.png"; }
OPTIPNG_ARGS=(-o7 -quiet)
[[ "$STRIP_META" == "1" ]] && OPTIPNG_ARGS+=(-strip all)
# --- Main loop ---
pass=0
while :; do
pass=$((pass+1))
echo "Durchlauf #$pass …"
changes=0
before_total=$(total_bytes)
while IFS= read -r -d '' file; do
[[ -w "$file" ]] || { echo " ⏭️ keine Schreibrechte: $file"; continue; }
orig_size=$(stat -c '%s' "$file" 2>/dev/null || echo 0)
tmp="$(make_tmp)"
if optipng "${OPTIPNG_ARGS[@]}" -out "$tmp" -- "$file" >/dev/null 2>&1; then
new_size=$(stat -c '%s' "$tmp" 2>/dev/null || echo 999999999)
if [[ "$new_size" -lt "$orig_size" ]]; then
[[ "$BACKUP_ORIGINALS" == "1" ]] && cp -p -- "$file" "$file.bak"
if mv -f -- "$tmp" "$file"; then
delta=$((orig_size - new_size))
echo " ✅ ${file}: -$(human "$delta") (neu: $(human "$new_size"))"
changes=$((changes+1))
else
echo " ⚠️ Konnte Datei nicht ersetzen: $file"
rm -f "$tmp"
fi
else
rm -f "$tmp"
fi
else
rm -f "$tmp"
fi
done < <(find "$TARGET_DIR" -type f -iname '*.png' -print0)
after_total=$(total_bytes)
if [[ "$changes" -eq 0 || "$after_total" -ge "$before_total" ]]; then
saved=$((before_total - after_total))
echo "Fertig nach $pass Durchlauf/Durchläufen."
echo "Gesamtersparnis: $(human "$saved") (neu: $(human "$after_total"))."
break
else
echo "Durchlauf #$pass abgeschlossen: $(human $((before_total - after_total))) eingespart."
fi
done
