Preise von Content Delivery Networks verstehen und optimieren
Content Delivery Networks (CDNs) sind unverzichtbar, um die Leistung von Anwendungen und Websites in der heutigen globalen digitalen Landschaft zu optimieren. Die Preismodelle hinter CDNs zu verstehen, kann jedoch komplex sein. Dieser Leitfaden schlüsselt aktuelle CDN-Preisstrukturen auf, erläutert die wichtigsten Kostentreiber und bietet praktische Strategien, um Ihre CDN-Ausgaben zu optimieren.

Was ist ein CDN?
Ein Content Delivery Network ist eine verteilte Gruppe von Servern, die strategisch rund um den Globus positioniert sind, um Inhalte schnell und zuverlässig auszuliefern. Indem sie Assets näher bei den Endnutzern zwischenspeichern, reduzieren CDNs die Latenz, verbessern die Ladezeiten und steigern das gesamte Nutzererlebnis.
Überblick über CDN-Preismodelle
CDN-Anbieter bieten in der Regel mehrere Preismodelle für unterschiedliche Anforderungen an. Preislisten ändern sich so häufig, dass jede hier abgedruckte Tabelle Sie schon nach wenigen Monaten in die Irre führen würde. Deshalb folgt hier nur die Grundform jedes Modells sowie die Seite mit den maßgeblichen Zahlen:
| Modell | Wie abgerechnet wird | Wofür geeignet |
|---|---|---|
| Monatlicher Pauschaltarif | Eine Gebühr pro Monat, Bandbreite wird nicht separat gemessen | Planbare Budgets, im Rahmen der Nutzungsbedingungen des Tarifs |
| Gestaffelter Egress pro GB | Der Stückpreis sinkt, sobald das Monatsvolumen veröffentlichte Stufen überschreitet | Gleichmäßige Auslieferung mit hohem Volumen |
| Pro Anfrage | Abrechnung je 10.000 oder je Million HTTP/S-Anfragen | Viele kleine Objekte, geringe Gesamtbytes |
| Regionsbasiert | Der Preis pro GB variiert je nach Auslieferungsregion | Zielgruppen außerhalb von Nordamerika und Europa |
| Vertrag mit Mindestabnahme | Rabatt im Austausch für ein monatliches oder jährliches Mindestvolumen | Bekannter, dauerhafter Grundlast-Traffic |
Prüfen Sie die aktuellen Zahlen direkt (Links geprüft im September 2026): Cloudflare-Tarife, Preise von Amazon CloudFront, Preise von Fastly und Preise von Akamai. Die meisten Anbieter veröffentlichen zudem einen Rechner, der sich zur Schätzung Ihrer eigenen Rechnung besser eignet als jede statische Tabelle.
Wichtige Faktoren, die die CDN-Kosten beeinflussen
Bandbreite und Datenübertragung
Der Bandbreitenverbrauch ist in der Regel der größte Posten der CDN-Ausgaben. Hochauflösende Medien, große Datei-Downloads und Streaming-Dienste können die Kosten für die Datenübertragung in die Höhe treiben. Jeder Anbieter mit verbrauchsabhängiger Abrechnung berechnet zudem je nach Auslieferungsregion einen anderen Preis pro GB, und die Spanne ist groß genug, um ein Budget zu verändern. Jeder hier genannte Prozentwert wäre bereits veraltet, sobald Sie ihn lesen. Entnehmen Sie die regionale Aufschlüsselung deshalb der Preisliste selbst: Sowohl Preise von CloudFront als auch Preise von Fastly enthalten eine Tabelle pro Region (geprüft im September 2026).
Geografische Verteilung
Die Auslieferungskosten variieren je nach geografischem Standort, da sich Infrastruktur und Betriebskosten unterscheiden. Wenn Sie die geografische Verteilung Ihrer Zielgruppe kennen, können Sie die Serverplatzierung und Ihre Routing-Strategien optimieren und so letztlich Kosten senken.
Funktionsumfang und Sicherheit
Moderne CDN-Anbieter bündeln fortgeschrittene Sicherheits- und Leistungsfunktionen, die sich auf den Preis auswirken können. Viele bieten inzwischen eine kostenlose, automatisierte Bereitstellung von SSL/TLS-Zertifikaten, robusten DDoS-Schutz auf Layer 7 sowie integrierte Optionen für eine Web Application Firewall (WAF). Diese Funktionen erhöhen die Sicherheit, ohne die Leistung zu beeinträchtigen.
Edge Computing und moderne Funktionen
Viele CDNs integrieren inzwischen Edge-Computing-Funktionen und ermöglichen so Datenverarbeitung in Echtzeit sowie serverlose Funktionen am Netzwerkrand. Mit diesem Ansatz führen Sie eigenen Code näher an Ihren Nutzern aus, optimieren die Auslieferung von Inhalten dynamisch und reduzieren dabei die Latenz.
Bildbytes in der Edge-Funktion selbst umzuwandeln, ist die verlockende Variante davon und zugleich
die teure: Sie bezahlen bei jeder Anfrage CPU-Zeit, und das Ergebnis lässt sich schlecht
zwischenspeichern, weil es von einem Request-Header abhängt und nicht von der URL. Die günstigere
Lösung schreibt die Variante in die URL. Ihr Origin oder Ihr Build-Schritt veröffentlicht
/images/w/320/photo.avif neben
/images/w/1024/photo.avif, und übrig bleibt nur noch die Auswahl, welche davon angefragt wird. Dieses
Node.js-Beispiel verwendet negotiator (npm install negotiator), um Media Ranges, Qualitätspräferenzen und
ausdrücklich abgelehnte Formate im Accept-Header der Anfrage zu berücksichtigen:
import Negotiator from 'negotiator'
// URL selection only. This converts nothing: your origin (or your build step)
// must already publish /images/w/320/... and /images/w/1024/... in each format,
// and your templates have to request the URL this returns. Because width and
// format land in the path, they are part of the cache key with no negotiation.
const WIDTHS = new Map([
['small', '320'],
['large', '1024'],
])
const SOURCE_EXTENSION = /\.(jpe?g|png)$/i
const PUBLIC_PREFIX = '/images/'
const VARIANT_PREFIX = '/images/w/'
function buildVariantUrl(requestUrl, accept) {
const url = new URL(requestUrl)
// Rewrite only the prefix that is public and has variants published under it,
// and never rewrite a variant URL again: /w/1024/w/1024/... does not exist.
if (!url.pathname.startsWith(PUBLIC_PREFIX)) return null
if (url.pathname.startsWith(VARIANT_PREFIX)) return null
const extension = url.pathname.match(SOURCE_EXTENSION)?.[1].toLowerCase()
if (extension === undefined) return null
// Prefer the original format when the client only offers a wildcard. Do not
// turn a PNG into JPEG (losing alpha), or select a format rejected with q=0.
const formats = new Map([
[extension === 'png' ? 'image/png' : 'image/jpeg', extension],
['image/avif', 'avif'],
['image/webp', 'webp'],
])
const accepted = new Negotiator({ headers: { accept: accept ?? '*/*' } }).mediaType([
...formats.keys(),
])
const format = formats.get(accepted)
if (format === undefined) return null
// A Map, so an unexpected ?w= value cannot reach an inherited object key such
// as 'constructor' and produce a width the origin never published.
const width = WIDTHS.get(url.searchParams.get('w')) ?? WIDTHS.get('large')
const name = url.pathname.slice(PUBLIC_PREFIX.length).replace(SOURCE_EXTENSION, `.${format}`)
const variant = new URL(url)
variant.pathname = `${VARIANT_PREFIX}${width}/${name}`
// Drop the query string outright. Nothing in it changes these bytes, and a
// surviving ?utm_source= would fragment the cache once per campaign.
variant.search = ''
return variant.toString()
}
Machen Sie sich klar, was das ist und was nicht. Es handelt sich um eine URL-Auswahl, also gehört sie überall dorthin, wo Sie Bild-URLs erzeugen: in eine Hilfsfunktion in einem Template, in einen Build-Schritt oder in eine Edge-Funktion, die mit einer Weiterleitung antwortet. Sie codiert kein Bild, und sie stellt nichts bei einem Anbieter bereit. Das Origin muss weiterhin jede Variante erzeugen und ausliefern, und das Frontend muss weiterhin die zurückgegebene URL anfragen, denn genau das schreibt die Breite und das Format in den Cache-Key.
Die Alternative, eine einzige URL auszuliefern und sie mit Vary: Accept zu kennzeichnen, ist schwächer, als sie
aussieht. Cloudflare dokumentiert das Zwischenspeichern je Variante bei einer Bildantwort mit
Vary: Accept als eigene Funktion,
Vary for Images,
die für Pro, Business und Enterprise aufgeführt ist, nicht jedoch für Free (geprüft im September
2026). Der Header allein verschafft Ihnen also noch keine Cache-Einträge pro Format; prüfen Sie, was
Ihr eigener Tarif und Ihr Anbieter tatsächlich tun, bevor Sie sich darauf verlassen. Wenn Sie das
Format im Pfad belassen, ist nirgends eine solche Einstellung nötig.
Die Lehre daraus lässt sich verallgemeinern: Jedes Byte, das eine Edge-Funktion umschreibt, ist CPU-Zeit, die Ihnen berechnet wird, und eine Antwort, deren Inhalt von einem Request-Header abhängt, ist eine Antwort, die Ihr CDN womöglich gar nicht zwischenspeichert.
Effektives Caching umsetzen
Effektives Caching minimiert unnötige Datenübertragungen und senkt die gesamten CDN-Kosten. Zwei Regeln erledigen den größten Teil der Arbeit, und beide sind enger gefasst als „statische Assets lange zwischenspeichern“.
Erstens ist eine Lebensdauer von einem Jahr in Verbindung mit immutable nur dann sicher, wenn sich die URL
ändert, sobald sich die Bytes ändern. Build-Tools liefern Ihnen das kostenlos über Content-Hashes wie app.7f3a91c2.js. Denselben
Header auf /logo.png anzuwenden bedeutet, dass irgendein Edge-Knoten weiterhin das Logo vom letzten
Jahr ausliefert, und Sie können es nicht zurückrufen.
Zweitens: Entscheiden Sie anhand der Route über die Cachefähigkeit, niemals anhand der Dateiendung.
/account/avatar.jpg und
/api/private.css enden beide auf eine Endung, die eine naive Regel als statisch einstuft, und beide
können Bytes enthalten, die zu einer einzelnen angemeldeten Person gehören. Wenn Sie die Richtlinie
an den Mount hängen, der Ihre Build-Ausgabe ausliefert, wird dieser Fehler unmöglich:
npm install express
npm pkg set type=module
// server.js - run with: node server.js
import express from 'express'
// A content hash in the filename is what makes 'immutable' safe: the URL is a
// promise that these bytes never change.
const FINGERPRINTED = /\.[0-9a-f]{8,}\.(?:css|js|jpe?g|png|gif|webp|avif|woff2?)$/
function cacheControlFor(filePath) {
if (FINGERPRINTED.test(filePath)) return 'public, max-age=31536000, immutable'
// Unversioned build output: let the edge serve it, but revalidate every hour
// so a replaced file is picked up in an hour rather than in a year.
return 'public, max-age=3600, stale-while-revalidate=86400'
}
const app = express()
// Everything under dist/assets is build output, so it is public by
// construction. setHeaders runs once the file has been resolved, which is why
// filePath is the real path rather than a guess made before routing.
app.use(
'/assets',
express.static('dist/assets', {
setHeaders: (res, filePath) => {
res.setHeader('Cache-Control', cacheControlFor(filePath))
},
}),
)
// Anything the mount did not serve is private until proven otherwise, including
// a 404 for a missing asset and every error below.
app.use((_req, res, next) => {
res.setHeader('Cache-Control', 'no-store')
next()
})
app.get('/account/avatar.jpg', (_req, res) => {
// Per-user bytes in a real app. The point here is the header above: the path
// ends in .jpg, and it still must not reach a shared cache.
res.type('jpg').send('per-user bytes')
})
app.get('/api/private.css', (_req, res) => {
res.type('css').send(':root{--brand:rebeccapurple}')
})
app.listen(3000)
Leistung überwachen und optimieren
Die Überwachung zentraler Leistungskennzahlen ist entscheidend, um die CDN-Kosten zu kontrollieren. Indem Sie Antwortzeiten, Cache-Hit-Raten, Übertragungsgrößen und Statuscodes verfolgen, erkennen Sie Ineffizienzen und können Ihre Auslieferung von Inhalten feinjustieren. Eine vertrauenswürdige Messsonde muss vier Dinge tun, die die Einzeiler-Variante auslässt: sich zeitlich begrenzen, einen Status außerhalb von 2xx als Fehler statt als gesunde Messung werten, den Body lesen, damit der gemeldete Wert eine Übertragungszeit und keine Time to First Byte ist, und die Verbindung auf jedem Pfad freigeben.
// edge-monitor.js - run with: node edge-monitor.js
import { setTimeout as sleep } from 'node:timers/promises'
async function probeEdge(url, timeoutMs = 5000) {
const start = performance.now()
const response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) })
try {
// A 503 from the edge is a monitoring signal, not a sample to average in.
if (!response.ok) return { url, ok: false, status: response.status }
// Reading to completion is what makes `duration` a transfer time. Awaiting
// fetch() alone stops at the response headers.
let bytes = 0
if (response.body) {
for await (const chunk of response.body) bytes += chunk.byteLength
}
return {
url,
ok: true,
status: response.status,
bytes,
duration: performance.now() - start,
cache: response.headers.get('cf-cache-status') ?? 'unknown',
}
} finally {
// A no-op once the body has been read. On the error path nothing consumed
// it, and an unread body holds the connection until the GC gets to it.
if (!response.bodyUsed) await response.body?.cancel()
}
}
// Log the origin and path, never the query string: a monitored URL can carry a
// signed token, and these lines end up in a log sink other people can read.
function formatProbe(result) {
const { origin, pathname } = new URL(result.url)
if (!result.ok) {
// An error name, not a message: fetch puts the full URL in `message`.
return `${origin}${pathname} FAIL ${result.error ?? `HTTP ${result.status}`}`
}
const duration = `${result.duration.toFixed(0)}ms`
return `${origin}${pathname} ${result.status} ${duration} ${result.bytes}B cache:${result.cache}`
}
// Sequential and stop-aware. setInterval with an async callback fires on a
// fixed clock whether or not the previous probe has finished, so a slow edge
// quietly turns your monitor into a load generator against it.
async function monitorEdge(url, signal, intervalMs = 300_000) {
while (!signal.aborted) {
const result = await probeEdge(url).catch((error) => ({ url, ok: false, error: error.name }))
console.log(formatProbe(result))
// Aborting the sleep rejects. That rejection is the stop signal, not a
// failure, and the loop condition above picks it up on the next pass.
await sleep(intervalMs, undefined, { signal }).catch(() => {})
}
}
const controller = new AbortController()
process.once('SIGINT', () => controller.abort())
await monitorEdge('https://cdn.example.com/assets/app.js', controller.signal)
cf-cache-status ist der Header von Cloudflare. Andere Anbieter verwenden eigene: Fastly und Akamai melden den
Cache-Zustand in X-Cache, und CloudFront nutzt X-Cache zusammen mit X-Amz-Cf-Pop. Lesen Sie jeweils den
Header, den Ihr Anbieter dokumentiert, und werten Sie einen fehlenden Header als unbekannt statt als
Miss.
Zwei ehrliche Einschränkungen. duration ist eine einzelne synthetische Messung von dort, wo der Prozess
läuft; sie misst also Ihren Weg zu einem PoP und nichts über die Wege Ihrer Nutzer. Behandeln Sie sie
als Regressionsalarm, nicht als Latenzwert, den Sie berichten. Und eine Messsonde ist selbst eine
kostenpflichtige Anfrage, weshalb das Standardintervall hier fünf Minuten statt fünf Sekunden
beträgt.
Strategien zur Optimierung der CDN-Kosten
Die Optimierung der CDN-Ausgaben erfordert sowohl möglichst geringe Datenübertragungen als auch den effektiven Einsatz von Caching.
Unnötige Datenübertragungen minimieren
Geringere Payload-Größen durch Komprimierung können die Kosten deutlich senken. Ergänzen Sie in der
weiter oben gezeigten Datei server.js vor dem statischen Mount eine Komprimierung, damit die dort
ausgelieferten Dateien beim Ausliefern komprimiert werden:
npm install compression
// Add to server.js, directly after `const app = express()`.
import compression from 'compression'
app.use(
compression({
filter: (req, res) => {
if (req.headers['x-no-compression']) return false
return compression.filter(req, res)
},
level: 6,
threshold: 0,
}),
)
Im Origin zu komprimieren ist die Rückfalllösung, nicht das Ziel: Die meisten CDNs können am Edge komprimieren und beide Komprimierungsvarianten zwischenspeichern, was Ihnen bei jedem Miss die CPU-Last erspart. Prüfen Sie, was Ihr Anbieter bereits tut, bevor Sie dafür doppelt bezahlen.
Caching effektiv nutzen
Normalisieren Sie über Header je Anfrage hinaus die Cache-Keys am Edge. Marketing-Query-Strings wie
?utm_source=newsletter erzeugen für identische Bytes pro Kampagne einen eigenen
Cache-Eintrag und machen so unbemerkt aus Hits lauter Misses. Jedes größere CDN bietet eine
Einstellung für den Cache-Key oder für den Umgang mit Query-Strings, um Parameter zu entfernen, die
die Antwort nicht verändern; sie zu konfigurieren ist meist der günstigste verfügbare Gewinn an
Cache-Hit-Rate. Die weiter oben gezeigte Hilfsfunktion buildVariantUrl wendet dieselbe Idee im Origin an, indem
sie einen Query-String gar nicht erst in eine Varianten-URL übernimmt.
Fazit
Die Optimierung der CDN-Kosten bedeutet, Leistung und Effizienz in ein Gleichgewicht zu bringen. Wenn Sie die verschiedenen Preismodelle verstehen, zentrale Kennzahlen überwachen, wirksame Caching-Strategien umsetzen und Edge-Computing-Funktionen nutzen, entwickeln Sie einen kosteneffizienten Ansatz für die Auslieferung von Inhalten.
Bei Transloadit setzen wir uns dafür ein, Entwicklern beim Aufbau effizienter und kosteneffizienter Anwendungen zu helfen. Leistungsstarke Lösungen zum Hochladen und Verarbeiten von Dateien finden Sie bei Transloadit.
