immerse: NGINX Modul für moderne Bildformatfilter
Erfordert den Pro-Plan (oder höher) des GetPageSpeed NGINX Extras Abonnements.
Installation
Sie können dieses Modul in jeder RHEL-basierten Distribution installieren, einschließlich, aber nicht beschränkt auf:
- RedHat Enterprise Linux 7, 8, 9 und 10
- CentOS 7, 8, 9
- AlmaLinux 8, 9
- Rocky Linux 8, 9
- Amazon Linux 2 und Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-immerse
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-immerse
Aktivieren Sie das Modul, indem Sie Folgendes an den Anfang von /etc/nginx/nginx.conf hinzufügen:
load_module modules/ngx_http_immerse_module.so;
Dieses Dokument beschreibt nginx-module-immerse v1.0.2, veröffentlicht am 05. April 2026.
NGINX Filtermodul für die transparente Bereitstellung moderner Bildformate. Interceptiert
Bildantworten aus jeder Quelle (statische Dateien, proxy_pass, FastCGI usw.)
und konvertiert sie basierend auf den Accept-Headern des Clients in WebP oder AVIF. Keine URL
Umleitungen, kein separater Dienst, keine Änderungen an der Anwendung.
Funktionsweise
ngx_immerse wird in die NGINX Filterkette eingefügt. Wenn eine Antwort mit
Content-Type: image/jpeg, image/png oder image/gif durchläuft, überprüft das
Modul den Accept-Header des Clients auf Unterstützung moderner Formate. Wenn eine
Übereinstimmung gefunden wird, wird entweder eine zwischengespeicherte Konvertierung bereitgestellt oder eine über einen
Thread-Pool ausgelöst - die Arbeitsprozesse bleiben nicht blockiert.
Client-Anfrage NGINX
| |
|--- GET /photo.jpg ---------> |
| Accept: image/avif, |
| image/webp |
| |--- Upstream / statische Datei
| |<-- image/jpeg Antwort
| |
| [ngx_immerse]
| |--- Cache-Treffer? cached avif bereitstellen
| |--- Cache-Fehlgeschlagen + lazy? jpeg bereitstellen,
| | Hintergrundkonvertierung in die Warteschlange stellen
| |--- Cache-Fehlgeschlagen + sync? in
| | Thread-Pool konvertieren, avif bereitstellen
| |
|<-- 200 image/avif ---------- |
| Vary: Accept |
Funktionen
- Transparente Formatverhandlung - analysiert den
Accept-Header gemäß RFC 7231 mit Unterstützung für Qualitätsfaktoren (q=0lehnt ab, höchstesqgewinnt) - WebP und AVIF Ausgabe - konfigurierbare Qualität, Prioritätsreihenfolge und bedingte Kompilierung (nur mit einem bauen, wenn gewünscht)
- Dateibasierter Cache - MD5-verschlüsselt mit der Quell-mtime im Schlüssel, sodass der Cache automatisch ungültig wird, wenn sich das Originalbild ändert
- Zwei Konvertierungsmodi -
lazy(jetzt Original bereitstellen, im Hintergrund konvertieren) undsync(inline konvertieren, modernes Format sofort bereitstellen) - Thread-Pool-Integration - Konvertierungen laufen in NGINX-Thread-Pools, wodurch die Ereignisschleife frei bleibt
- Sanfter Rückfall - Konvertierungsfehler, beschädigte Bilder, fehlender Thread-Pool, voller Speicher: immer das Original bereitstellen, niemals 500 zurückgeben
- Größenschwellen - Bilder unter
immerse_min_sizeoder überimmerse_max_sizeüberspringen, um CPU bei winzigen Icons oder riesigen Assets nicht zu verschwenden - CDN-sicher - fügt
Vary: Accepthinzu, damit Caches und CDNs nicht das falsche Format an den falschen Client ausliefern - Debug-Header -
X-Immerse: hit|miss|error|passzeigt an, was passiert ist (umschaltbar) - Magische Byte-Erkennung - identifiziert das Eingabeformat anhand der Dateisignatur, nicht der URL-Erweiterung
Konfiguration
Minimales Beispiel
thread_pool immerse threads=4;
http {
immerse_cache_path /var/cache/nginx/immerse levels=1:2 max_size=1g;
server {
listen 80;
location /images/ {
immerse on;
immerse_thread_pool immerse;
alias /var/www/images/;
}
}
}
Mit proxied Inhalten
thread_pool immerse threads=4;
http {
immerse_cache_path /var/cache/nginx/immerse levels=1:2 max_size=1g;
server {
listen 80;
location /api/photos/ {
immerse on;
immerse_mode sync;
immerse_thread_pool immerse;
proxy_pass http://backend;
}
}
}
Vollständiges Beispiel mit allen Direktiven
thread_pool immerse threads=4;
http {
immerse_cache_path /var/cache/nginx/immerse levels=1:2
max_size=2g inactive=60d;
# Standardwerte für alle Standorte
immerse_formats avif webp;
immerse_webp_quality 82;
immerse_avif_quality 63;
server {
listen 80;
# Statische Bilder - lazy-Modus (Standard)
location /images/ {
immerse on;
immerse_thread_pool immerse;
immerse_min_size 2k;
immerse_max_size 5m;
alias /var/www/images/;
}
# Proxied Bilder - sync-Modus für sofortige Konvertierung
location /api/photos/ {
immerse on;
immerse_mode sync;
immerse_thread_pool immerse;
proxy_pass http://backend;
}
# Nur WebP (kein AVIF)
location /thumbnails/ {
immerse on;
immerse_formats webp;
immerse_webp_quality 75;
immerse_thread_pool immerse;
alias /var/www/thumbs/;
}
# Debug-Header in der Produktion deaktivieren
location /cdn/ {
immerse on;
immerse_x_header off;
immerse_thread_pool immerse;
alias /var/www/cdn/;
}
}
}
Direktivenreferenz
immerse
Syntax: immerse on | off;
Standard: off
Kontext: location
Aktiviert oder deaktiviert die Konvertierung von Bildformaten für den Standort. Wenn aktiviert, interceptiert das Modul Bildantworten und versucht eine Konvertierung basierend auf der Unterstützung des Clients.
Erfordert, dass immerse_cache_path auf der http-Ebene gesetzt ist. Wenn der Cache-Pfad nicht konfiguriert ist, protokolliert das Modul einen Fehler und leitet die Antwort unverändert weiter.
immerse_cache_path
Syntax: immerse_cache_path path [levels=levels] [max_size=size] [inactive=time];
Standard: keine (erforderlich, wenn immerse aktiviert ist)
Kontext: http
Setzt das Cache-Verzeichnis und die Parameter. Diese Direktive ist erforderlich - das Modul wird keine Bilder konvertieren, ohne einen konfigurierten Cache-Pfad.
Parameter:
- path - Verzeichnis im Dateisystem für zwischengespeicherte Konvertierungen. Wird automatisch erstellt, wenn es nicht existiert.
- levels - Tiefe der Unterverzeichnishierarchie, angegeben als durch Doppelpunkt getrennte Ziffern (1 oder 2). Standard:
1:2. Mitlevels=1:2wird ein Cache-Schlüssela3b1c4d5e6...unterpath/a/3b/a3b1c4d5e6....webpgespeichert. - max_size - maximale Gesamtgröße des Caches. Akzeptiert Größen-Suffixe (
k,m,g). Standard: nicht gesetzt (kein Limit). - inactive - Zeit, nach der ungenutzte zwischengespeicherte Dateien zur Entfernung berechtigt sind. Akzeptiert Zeit-Suffixe (
s,m,h,d). Standard:30d.
immerse_cache_path /var/cache/nginx/immerse levels=1:2 max_size=1g inactive=30d;
immerse_formats
Syntax: immerse_formats format ...;
Standard: avif webp
Kontext: http, server, location
Setzt die bevorzugten Ausgabeformate in Prioritätsreihenfolge. Wenn mehrere Formate vom Client mit gleichen Qualitätsfaktoren akzeptiert werden, gewinnt das erste hier aufgeführte Format.
Gültige Formate: avif, webp. Mindestens eines muss zur Kompilierungszeit unterstützt werden.
# Bevorzuge WebP über AVIF
immerse_formats webp avif;
# Nur WebP
immerse_formats webp;
immerse_mode
Syntax: immerse_mode lazy | sync;
Standard: lazy
Kontext: location
Setzt die Konvertierungsstrategie für Cache-Fehlgeschlagen.
lazy - stellt das Originalbild sofort ohne Latenzüberhang bereit. Wenn die Bildquelle dateibasiert ist, wird eine Hintergrundkonvertierung im Thread-Pool in die Warteschlange gestellt. Die konvertierte Variante ist für nachfolgende Anfragen verfügbar. Am besten für die Bereitstellung statischer Dateien, bei denen die Latenz der ersten Anfrage wichtig ist.
sync - puffert den gesamten Antwortkörper, konvertiert ihn im Thread-Pool und stellt das konvertierte Bild in derselben Anfrage bereit. Der Worker wird nicht blockiert (der Thread-Pool übernimmt die Arbeit). Am besten für proxied Inhalte oder wenn Sie möchten, dass jede Antwort in einem modernen Format vorliegt.
# Statische Dateien - lazy ist in Ordnung, Cache wird schnell warm
location /images/ {
immerse on;
immerse_mode lazy;
}
# API-Antworten - sync stellt sicher, dass das moderne Format bei der ersten Anfrage vorliegt
location /api/photos/ {
immerse on;
immerse_mode sync;
proxy_pass http://backend;
}
immerse_webp_quality
Syntax: immerse_webp_quality quality;
Standard: 80
Kontext: http, server, location
WebP Kodierungsqualität (1-100). Höhere Werte erzeugen eine bessere visuelle Qualität bei größeren Dateigrößen. Werte um 75-85 bieten ein gutes Gleichgewicht für die meisten Inhalte.
immerse_avif_quality
Syntax: immerse_avif_quality quality;
Standard: 60
Kontext: http, server, location
AVIF Kodierungsqualität (1-100). AVIF erreicht eine gute visuelle Qualität bei niedrigeren numerischen Werten als WebP oder JPEG. Werte um 50-70 sind typisch für die Webbereitstellung. Der Encoder verwendet Geschwindigkeit 6 (ausgewogenes Verhältnis von Geschwindigkeit/Qualität).
immerse_min_size
Syntax: immerse_min_size size;
Standard: 1k (1024 Bytes)
Kontext: http, server, location
Mindestgröße des Antwortkörpers für die Konvertierung. Bilder kleiner als dies werden unverändert weitergeleitet. Dies vermeidet, CPU bei winzigen Bildern (Favicon, 1x1 Tracking-Pixel) zu verschwenden, bei denen die Formatkonvertierung vernachlässigbare Einsparungen bringt.
immerse_max_size
Syntax: immerse_max_size size;
Standard: 10m (10485760 Bytes)
Kontext: http, server, location
Maximale Größe des Antwortkörpers für die Konvertierung. Bilder größer als dies werden unverändert weitergeleitet. Dies verhindert Ressourcenerschöpfung durch sehr große Bilder, die während der Dekodierung/Kodierung signifikanten Speicher und CPU verbrauchen würden.
immerse_thread_pool
Syntax: immerse_thread_pool name;
Standard: default
Kontext: http, server, location
Name des NGINX Thread-Pools, der für Konvertierungsaufgaben verwendet werden soll. Muss mit einer
thread_pool-Direktive im Hauptkontext übereinstimmen.
# Definiere einen dedizierten Pool
thread_pool immerse threads=4;
http {
server {
location /images/ {
immerse on;
immerse_thread_pool immerse;
}
}
}
Größenrichtlinien: Beginnen Sie mit der Anzahl der CPU-Kerne. Die Bildkodierung ist CPU-gebunden, sodass mehr Threads als Kerne keinen Vorteil bieten. Wenn derselbe Server andere Thread-Pool-Arbeiten (aio) behandelt, ziehen Sie einen dedizierten Pool für immerse in Betracht.
immerse_x_header
Syntax: immerse_x_header on | off;
Standard: on
Kontext: http, server, location
Steuert den X-Immerse Antwortheader. Wenn aktiviert, enthält jede verarbeitete
Antwort einen Header, der angibt, was passiert ist:
| Wert | Bedeutung |
|---|---|
hit |
Aus dem Cache bereitgestellt |
miss |
Cache-Fehlgeschlagen; konvertiert (sync) oder Original bereitgestellt (lazy) |
error |
Konvertierung fehlgeschlagen; Original als Rückfall bereitgestellt |
Deaktivieren Sie dies in der Produktion, wenn Sie nicht den internen Modulstatus an die Clients weitergeben möchten.
Antwortheader
Wenn ngx_immerse eine Antwort verarbeitet, ändert oder fügt es die folgenden Header hinzu:
| Header | Wert | Wann |
|---|---|---|
Content-Type |
image/webp oder image/avif |
Konvertiert oder aus dem Cache bereitgestellt |
Content-Length |
Größe des konvertierten Bildes | Konvertiert oder aus dem Cache bereitgestellt |
Vary |
Accept |
Immer (auch bei Durchleitung), wenn das Modul aktiv ist |
X-Immerse |
hit, miss oder error |
Wenn immerse_x_header aktiviert ist |
Der Vary: Accept Header ist entscheidend für das korrekte Verhalten von CDNs. Ohne ihn
kann ein CDN eine WebP-Antwort zwischenspeichern und sie an einen Client ausliefern, der nur JPEG unterstützt.
Analyse des Accept-Headers
Das Modul analysiert den Accept-Anforderungsheader gemäß RFC 7231:
- Extrahiert
image/webpundimage/avifEinträge mit ihren Qualitätsfaktoren q=0bedeutet, dass der Client dieses Format ausdrücklich ablehntq=1(oder keinq-Parameter) bedeutet volle Unterstützung- Das Format mit dem höchsten
q-Wert wird ausgewählt - Bei gleichem
qgewinnt das erste Format inimmerse_formats - Wenn keines der Formate vorhanden ist oder beide
q=0haben, wird das Original bereitgestellt
Beispiele:
| Accept-Header | Ergebnis (mit Standard immerse_formats avif webp) |
|---|---|
image/avif, image/webp |
AVIF (erstes in der Konfiguration, gleiches q) |
image/webp |
WebP |
image/avif;q=0.8, image/webp;q=0.9 |
WebP (höheres q) |
image/avif;q=0, image/webp |
WebP (AVIF abgelehnt) |
text/html, image/jpeg |
Original (kein modernes Format) |
Eingabeformat-Erkennung
Quellbilder werden anhand magischer Bytes im Antwortkörper identifiziert, nicht anhand der Dateierweiterung:
| Format | Magische Bytes |
|---|---|
| JPEG | FF D8 FF |
| PNG | 89 50 4E 47 0D 0A 1A 0A |
| GIF | GIF87a oder GIF89a |
Bilder, die bereits im WebP- oder AVIF-Format vorliegen, werden unverändert weitergeleitet. Animierte GIFs (mehrere Frames) werden ebenfalls unverändert weitergeleitet.
Cache
Funktionsweise
Der Cache speichert konvertierte Bilder in einer Verzeichnisstruktur, die nach MD5-Hash
verschlüsselt ist. Der Hash-Eingang ist URI + source_mtime + target_format + quality, sodass:
- Verschiedene Formate (WebP, AVIF) erhalten separate Cache-Einträge
- Änderungen der Qualitätseinstellungen erzeugen neue Cache-Einträge
- Änderungen am Originalbild (Änderung seiner mtime) machen die zwischengespeicherte Konvertierung automatisch ungültig
Verzeichnislayout
Mit levels=1:2 produziert ein Cache-Schlüssel a3b1c4d5...:
/var/cache/nginx/immerse/a/3b/a3b1c4d5e6f7890123456789abcdef01.webp
Atomare Schreibvorgänge
Cache-Dateien werden atomar geschrieben: Daten gehen zuerst in eine temporäre Datei, dann
verschiebt rename() sie an ihren Platz. Dies verhindert, dass teilweise geschriebene Dateien
unter gleichzeitiger Last bereitgestellt werden.
Cache-Warmup
Im lazy-Modus stellt die erste Anfrage für ein Bild das Original bereit. Die
Konvertierung läuft im Hintergrund, und nachfolgende Anfragen erhalten das zwischengespeicherte
moderne Format. Im sync-Modus löst die allererste Anfrage die Konvertierung aus und
stellt das Ergebnis bereit.
Manuelles Leeren des Caches
Um den gesamten Cache zu leeren:
rm -rf /var/cache/nginx/immerse/*
Ein NGINX-Neuladen ist nicht erforderlich. Das Modul wird die Verzeichnisse nach Bedarf neu erstellen.
Fehlerbehandlung
ngx_immerse folgt einer strengen "nie brechen, was bereits funktioniert"-Politik:
| Bedingung | Verhalten |
|---|---|
| Konvertierung schlägt fehl (Codec-Fehler) | Original bereitstellen, Fehler protokollieren |
| Cache-Schreiben schlägt fehl (Speicher voll, Berechtigungen) | Aus dem Speicher konvertiert bereitstellen, Warnung protokollieren |
| Beschädigtes oder abgeschnittenes Eingabebild | Original bereitstellen, Fehler protokollieren |
Bild unter min_size oder über max_size |
Unverändert weiterleiten |
Kein modernes Format im Client Accept |
Unverändert weiterleiten, Vary: Accept hinzufügen |
| Thread-Pool nicht gefunden | Rückfall auf synchrone (blockierende) Konvertierung |
immerse_cache_path nicht konfiguriert |
Unverändert weiterleiten, Fehler protokollieren |
| Unbekanntes Bildformat (nicht JPEG/PNG/GIF) | Unverändert weiterleiten |
Das Modul wird niemals einen 500-Fehler aufgrund eines Konvertierungsfehlers zurückgeben.
Architektur
Quelldateien
| Datei | Zweck |
|---|---|
config |
NGINX-Bausystemintegration, Bibliotheksdetektion |
src/ngx_http_immerse_common.h |
Gemeinsame Typen, Konstanten, Funktionsdeklarationen |
src/ngx_http_immerse_module.c |
Modul-Einstiegspunkt, Direktiven, Konfigurationslebenszyklus |
src/ngx_http_immerse_filter.c |
Header- und Body-Filterkette, Zustandsmaschine, Thread-Pool-Dispatch |
src/ngx_http_immerse_convert.c |
Bilddekodierungs-/Kodierungs-Engine (läuft im Thread-Pool) |
src/ngx_http_immerse_cache.c |
Cache-Schlüsselgenerierung, Lookup, atomare Speicherung |
src/ngx_http_immerse_accept.c |
RFC 7231 Accept-Header-Parser |
src/ngx_http_immerse_util.c |
Antwortversand, Body-Pufferung, Format-Erkennung, Header-Hilfsfunktionen |
Thread-Sicherheit
Alle Arbeiten zur Bildkonvertierung laufen in NGINX-Thread-Pool-Workern. Der Konvertierungscode (ngx_http_immerse_convert.c) verwendet nur malloc/free, POSIX-Datei-I/O
und Aufrufe der Bildbibliothek. Er greift niemals auf den gemeinsamen Zustand von NGINX, Anfrage-Pools oder die Ereignisschleife zu.
Die Ergebnisse werden über den Standard-NGINX-Thread-Aufgabenabschlussmechanismus (ngx_thread_task_t) an die Haupt-Ereignisschleife zurückgegeben.
Zustandsmaschine
Der Body-Filter verwendet eine phasenbasierte Zustandsmaschine:
START -> READ -> CONVERT -> SEND -> DONE (sync-Modus)
PASS -> DONE (lazy-Modus, erste Anfrage)
SERVE_CACHE -> DONE (Cache-Treffer)
Tests
Docker-basiert (empfohlen)
# Alle Tests ausführen (HUP-Modus für ~10x schnellere Iteration)
make tests
# Eine bestimmte Testdatei ausführen
make tests T=t/sync.t
# Ohne HUP-Modus ausführen (sauberer Zustand zwischen den Tests)
make tests HUP=0
# Interaktive Shell zum Debuggen
make shell
# Basisbild neu erstellen (nach Änderungen an Dockerfile)
make base-image
# Gegen eine andere NGINX-Version testen
make tests NGINX_VERSION=release-1.26.2
CI
GitHub Actions führt Tests gegen NGINX 1.26.2, 1.27.3 und 1.28.0 bei jedem Push und Pull-Request aus.
Test-Suite
| Datei | Abdeckung |
|---|---|
t/accept.t |
Analyse des Accept-Headers, q-Werte, Format-Auswahl |
t/sync.t |
Sync-Modus-Konvertierung für JPEG, PNG, GIF in WebP/AVIF |
t/lazy.t |
Lazy-Modus: Original zuerst bereitgestellt, Cache danach befüllt |
t/cache.t |
Cache Treffer/Fehlgeschlagen Verhalten |
t/limits.t |
Filterung von min_size und max_size |
t/fallback.t |
Rückfall bei beschädigten Bildern |
t/passthrough.t |
Modul deaktiviert, Nicht-Bild-Inhalt, kein Accept-Header |
t/config.t |
Direktivenvalidierung, x_header Umschaltung |
t/vary.t |
Anwesenheit des Vary: Accept Headers |
Debugging
- Überprüfen Sie
test-error.logim Repo-Stammverzeichnis für NGINX-Debug-Ausgaben - Verwenden Sie
make shell, um in den Container zu gelangen und Tests manuell auszuführen - Der Log-Level ist im Docker-Testumfeld auf
debuggesetzt