Aufbau einer Bildoptimierungs-Pipeline mit einem CDN
Eine Bild-Pipeline kombiniert Speicher, Transformationen, responsives Markup und Caching. Ein CDN kann den Aufwand für die Auslieferung reduzieren, doch Transformations-URLs und Signierregeln sind anbieterspezifisch. Dieser Leitfaden verwendet die Hosted-Image-Schnittstelle von Cloudflare Images und macht diese Vorgaben explizit.
Bild-CDNs verstehen
Bilddienste können Varianten in verschiedenen Größen erzeugen, unterstützte Formate aushandeln und die Ergebnisse cachen. Prüfen Sie, welche Vorgänge beim Upload und welche bei der Auslieferung stattfinden, welche Formate unterstützt werden und wie Cache-Keys und Zugriffskontrollen zusammenwirken. Nicht jedes CDN führt ohne einen separat konfigurierten Transformationsdienst eine Bildverarbeitung durch.
Einen Anbieter für Bildoptimierung auswählen
Vergleichen Sie unterstützte Formate, das Verhalten bei der Größenänderung, private Auslieferung, betriebliche Limits und die Integration mit Ihrem Speicher. Die folgenden Beispiele verwenden die vordefinierten Varianten von Cloudflare Images, nicht dessen separate API zur Transformation entfernter Bilder. Verwenden Sie die dokumentierten URL- und Signaturvorgaben Ihres gewählten Anbieters, statt Query-Parameter von Bild-CDNs als austauschbar zu behandeln.
Azure Front Door für die Bildauslieferung nutzen
Azure Front Door kann Inhalte von einem Ursprung mit Bildverarbeitung ausliefern, doch Bilder dahinter zu platzieren erzeugt für sich genommen noch keine größenangepassten Varianten. Halten Sie Transformationsdienst, Zugriffsrichtlinie für den Ursprung und Cache-Konfiguration getrennt. Die Einrichtung von Ursprung und Auslieferung dieses Anbieters finden Sie in der Dokumentation zu Azure Front Door.
Bildoptimierung einrichten
Laden Sie ein Bild zu Cloudflare Images hoch und ermitteln Sie dessen Account-Hash und Image-ID.
Erstellen Sie in dessen Dashboard vordefinierte Varianten mit den Namen w320, w640 und w1280, jeweils mit
den passenden Breiten, erhaltenem Seitenverhältnis und dem Fit-Modus scale-down. Siehe die
Dokumentation zur Einrichtung von Varianten.
Verwenden Sie für dieses Beispiel ein Quellbild mit mindestens 1.280 Pixeln Breite, damit jeder
Breitendeskriptor im Attribut srcset einer tatsächlichen Ausgabebreite entspricht.
Speichern Sie diesen gemeinsam genutzten URL-Builder als images.ts. Die Variantenkonfiguration wird
einmal definiert und für responsive Auslieferung und Signierung wiederverwendet. Dieses Beispiel
unterstützt bewusst opake Image-IDs ohne benutzerdefinierte Pfadsegmente.
export const imageVariants = [
{ name: 'w320', width: 320 },
{ name: 'w640', width: 640 },
{ name: 'w1280', width: 1280 },
]
export function getOptimizedImageUrl(accountHash: string, imageId: string, variant: string): string {
const segment = /^[A-Za-z0-9_-]{1,128}$/
if (!segment.test(accountHash) || !segment.test(imageId)) {
throw new Error('Use an account hash and opaque image ID, not a URL or path')
}
if (!imageVariants.some(({ name }) => name === variant)) {
throw new Error('Unknown image variant')
}
return new URL(`https://imagedelivery.net/${accountHash}/${imageId}/${variant}`).href
}
Auslieferungs-URLs für Hosted Images enthalten Account-Hash, Image-ID und Variante im Pfad.
Beliebige Query-Parameter (width oder format) an eine unvollständige URL anzuhängen ergibt keine
gleichwertige API. Cloudflare kann Auslieferungsformate anhand der Browser-Anfrage aushandeln;
behalten Sie die vollständigen
Vorgaben für die Hosted-Image-Auslieferung
im Blick, wenn Sie einen weiteren Proxy oder Cache hinzufügen.
Responsive Bilder implementieren
Halten Sie die React-Komponente unabhängig davon, ob URLs öffentlich oder signiert sind. Übergeben
Sie ihr die generierten Quellen, die intrinsischen Abmessungen und einen Wert für sizes, der zum
tatsächlichen Layout passt:
import type { ReactNode } from 'react'
interface ResponsiveImageProps {
sources: { url: string; width: number }[]
alt: string
width: number
height: number
sizes: string
loading?: 'lazy' | 'eager'
}
export function ResponsiveImage({
sources, alt, width, height, sizes, loading = 'lazy',
}: ResponsiveImageProps): ReactNode {
const fallback = sources[sources.length - 1]
if (!fallback || !Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) {
throw new Error('Supply image sources and positive intrinsic dimensions')
}
return (
<img
src={fallback.url}
srcSet={sources.map((source) => `${source.url} ${source.width}w`).join(', ')}
width={width}
height={height}
alt={alt}
sizes={sizes}
loading={loading}
className="responsive-image"
/>
)
}
Wenden Sie im Stylesheet Ihrer Anwendung eine responsive CSS-Regel an:
.responsive-image {
display: block;
max-width: 100%;
height: auto;
}
Erstellen Sie für öffentliche Bilder aus der gemeinsamen Variantenliste die Prop sources:
import { getOptimizedImageUrl, imageVariants } from './images.ts'
export function publicImageSources(accountHash: string, imageId: string) {
return imageVariants.map(({ name, width }) => ({
width,
url: getOptimizedImageUrl(accountHash, imageId, name),
}))
}
Die Props width und height beschreiben das Seitenverhältnis der Quelle und reservieren Platz im
Layout. Verwenden Sie für ein wahrscheinliches LCP-Bild Eager Loading, statt jedes Bild verzögert zu
laden. Behalten Sie aussagekräftigen Alternativtext bei, auch wenn die Auslieferung fehlschlägt;
wiederholen Sie den Abruf einer nicht existierenden Fallback-URL nicht endlos.
Performance überwachen
Verwenden Sie das aktuelle Paket web-vitals und erfassen Sie LCP, INP und CLS. INP ersetzt die
eingestellte Metrik FID. Rufen Sie diesen ausschließlich im Browser laufenden Initialisierer einmal
auf, nachdem die Einwilligungsrichtlinie Ihrer Anwendung für Analytics erfüllt ist. Implementieren
Sie den Endpunkt /analytics im selben Ursprung, bevor Sie die Auslieferung aktivieren.
import { onCLS, onINP, onLCP, type Metric } from 'web-vitals'
function sendToAnalytics({ name, value, id }: Pick<Metric, 'name' | 'value' | 'id'>): void {
const body = JSON.stringify({ name, value, id })
if (typeof navigator.sendBeacon === 'function' && navigator.sendBeacon('/analytics', body)) return
void fetch('/analytics', { body, method: 'POST', keepalive: true })
.then((response) => {
if (!response.ok) throw new Error('Analytics request failed')
})
.catch(() => console.warn('Could not deliver performance metric'))
}
export function initializePerformanceMonitoring(): void {
onLCP(sendToAnalytics)
onINP(sendToAnalytics)
onCLS(sendToAnalytics)
}
Ein eingereihtes Beacon ist kein Beleg für den serverseitigen Empfang. Vergleichen Sie Feldmetriken mit kontrollierten Browser-Messungen und den Fehlerraten für Bildanfragen. Senden Sie keine vollständigen signierten Bild-URLs und keine Nutzerinhalte als Metrik-Identifikatoren an Analytics.
Sicherheitsaspekte
Die Signierung erfolgt nur auf dem Server, nachdem der Aufrufer authentifiziert und der Zugriff auf das Bild autorisiert wurde. Speichern Sie den Signaturschlüssel von Images als Server-Secret; er ist weder der Account-Hash noch ein API-Token und darf niemals in Browser-Bundles gelangen. Private Bilder müssen signierte URLs erfordern, und ihre Varianten dürfen nicht so konfiguriert sein, dass sie diese Anforderung umgehen.
Speichern Sie diesen ausschließlich für Node.js gedachten Helper als images.server.ts. Er verwendet denselben
Pfad-Builder wieder, ergänzt den Parameter exp des Anbieters und signiert den vollständigen Pfad
samt Query, bevor sig hinzugefügt wird:
import { createHmac } from 'node:crypto'
import { getOptimizedImageUrl } from './images.ts'
export function getSecureImageUrl(
accountHash: string,
imageId: string,
variant: string,
signingKey: string,
expiresIn = 3600,
): string {
if (!signingKey || !Number.isInteger(expiresIn) || expiresIn < 1 || expiresIn > 86400) {
throw new Error('A signing key and bounded expiry are required')
}
const url = new URL(getOptimizedImageUrl(accountHash, imageId, variant))
url.searchParams.set('exp', String(Math.floor(Date.now() / 1000) + expiresIn))
const payload = `${url.pathname}?${url.searchParams.toString()}`
url.searchParams.set('sig', createHmac('sha256', signingKey).update(payload).digest('hex'))
return url.href
}
Erzeugen Sie bei privaten Bildern jede responsive Quell-URL serverseitig und übergeben Sie der Komponente nur diese signierten URLs. Wenn Sie Variante oder Ablaufzeit ändern, ändert sich auch die signierte Payload. Folgen Sie der Dokumentation zur Signierung privater Bilder und stellen Sie keinen öffentlichen Signierdienst bereit, der beliebige Image-IDs ohne Besitzprüfung akzeptiert. Eine Signatur ist bis zum Ablauf eine Inhaberberechtigung und kein Ersatz für Autorisierung.
Konfigurieren Sie auf Seitenebene eine Content Security Policy, die den tatsächlichen
Auslieferungsursprung zulässt, etwa https://imagedelivery.net. Eine Wildcard für dessen Subdomains schließt den reinen
Hostnamen nicht ein. Führen Sie diese Direktive mit der vollständigen Policy der Anwendung zusammen,
statt nicht zugehörige Direktiven zu ersetzen. Aktivieren Sie nur HSTS-Richtlinien, die Ihr gesamter
betroffener Domain-Bereich über HTTPS unterstützen kann.
Bewährte Verfahren
- Leiten Sie responsive URLs aus einer verifizierten Variantenkonfiguration ab.
- Stimmen Sie
sizesauf das Layout ab und Breitendeskriptoren auf tatsächliche Ausgabebreiten. - Erhalten Sie das intrinsische Seitenverhältnis, um Layout-Verschiebungen zu reduzieren.
- Halten Sie Signaturschlüssel serverseitig und autorisieren Sie den Zugriff, bevor Sie private URLs ausgeben.
- Testen Sie Cache-Verhalten und Ablauf; eine Signatur hinzuzufügen macht ein öffentlich konfiguriertes Bild nicht privat.
- Vergleichen Sie tatsächliche Dateigrößen, visuelle Qualität und Seitenmetriken, statt eine universelle Beschleunigung zu versprechen.
Fazit
Eine zuverlässige Bild-Pipeline nutzt die tatsächlichen URL- und Signaturvorgaben des Anbieters, responsives Markup und ein beobachtbares Auslieferungsverhalten. Halten Sie die gemeinsame Variantenkonfiguration zentral und testen Sie öffentliche und private Abläufe getrennt. Für verwaltete Verarbeitung und Auslieferung sehen Sie sich Transloadits Smart CDN an.
