effidevFlutter · Cloudflare-Edge · Cloud-Kostenoptimierung
Deutsch

Cloudflare Pages zu Workers Static Assets Migration

Cloudflare Pages zu Workers Static Assets Migrationsarchitektur

Seit Ende 2025 hat Cloudflare offiziell angekündigt, Pages in den Wartungsmodus (Maintenance Mode) zu versetzen und alle künftigen Investitionen sowie neuen Funktionen auf die Workers-Plattform zu konzentrieren. Bestehende Pages-Projekte funktionieren zwar weiterhin, aber neue Funktionen wie Cron Triggers, verbesserte Observability und direkte Durable Objects-Bindungen stehen ausschließlich auf Workers zur Verfügung.

Dieser Artikel behandelt den gesamten Ablauf der unterbrechungsfreien Migration von statischen Websites und Fullstack-Apps von Cloudflare Pages zu Workers Static Assets. Da dieser Blog (effidev.dev) selbst als Pages-Projekt mit pages_build_output_dir: "dist" betrieben wird, ist dieser Leitfaden aus der Perspektive der praktischen Durchführung genau dieser Migration geschrieben.

Kernzusammenfassung

  • Pages ist im Wartungsmodus: Neue Funktionen und Optimierungen wurden eingestellt. Workers ist der offiziell empfohlene Weg, um statische Assets und dynamische Logik in einer einzigen Bereitstellungseinheit auszuliefern.
  • Statische Asset-Anfragen kostenlos & unbegrenzt: Statische Dateien (HTML, CSS, Bilder), die über Workers Static Assets bereitgestellt werden, zählen nicht als Worker-Aufrufe (Invocations), sodass keine Gebühren anfallen.
  • Dateilimit von 100.000 Dateien: Im Paid-Tarif werden bis zu 100.000 Dateien pro Worker-Version und bis zu 25 MiB pro Einzeldatei unterstützt (Wrangler 4.34.0+).
  • Eine Zeile Änderung in wrangler.jsonc: Ersetzen Sie pages_build_output_dir durch assets.directory, um die Kernumstellung abzuschließen.
  • Integration von Framework-Adaptern: Für SSR-Frameworks wie Astro, Next.js und SvelteKit müssen Sie lediglich einen Cloudflare Workers-kompatiblen Adapter konfigurieren, damit statische Assets und serverseitige Logik als ein einziger Worker bereitgestellt werden.

1. Pages vs. Workers Static Assets: Was ändert sich?

Dies sind die wesentlichen Unterschiede laut dem offiziellen Cloudflare-Migrationsleitfaden.

Vergleichspunkt Cloudflare Pages Workers Static Assets
Plattformstatus Wartungsmodus (keine neuen Funktionen) Aktive Entwicklung + Fokus auf Investitionen
Bereitstellung statischer Assets Integriert Gleicher Support über assets.directory-Einstellung
Serverseitige Logik _worker.js (eingeschränkt) Vollständiges Worker-Skript (unbegrenzte Bindungen)
Durable Objects Keine direkte Bindung möglich Direkte Bindung möglich
Cron Triggers Nicht unterstützt Unterstützt
Observability Nur Basis-Logs Workers Logs, Tail Workers, Logpush
Dateilimit 20.000 Dateien 100.000 Dateien (Paid-Tarif)
Deployment-Befehl wrangler pages deploy wrangler deploy
Gebühren für statische Assets Kostenlos Kostenlos (kein Worker-Aufruf)

Der entscheidende Unterschied ist die direkte Bindung von Durable Objects und Cron Triggers. Bei Pages mussten diese in ein separates Worker-Projekt ausgelagert werden. Mit Workers Static Assets können Sie die Bereitstellung statischer Assets, DO, Cron, D1 und R2 in einer einzigen wrangler.jsonc konfigurieren.

2. Checkliste vor der Migration

Dies sind die Punkte, die Sie vor der Umstellung überprüfen sollten. Wenn auch nur ein Punkt nicht erfüllt ist, kann es nach der Migration zu Ausfällen kommen.

Checkliste Überprüfungsmethode
Wrangler-Version ≥ 4.34.0 npx wrangler --version
Build-Ausgabeverzeichnis prüfen ls dist/ oder Build-Ausgabepfad des Frameworks
Verwendung von _worker.js Bei Nutzung im Pages Advanced Mode ist eine Umstellung auf ein Worker-Skript erforderlich
DNS-Einstellungen für benutzerdefinierte Domains CNAME-Einträge des Pages-Projekts auf Workers-Routen umstellen
_headers- / _redirects-Dateien Funktionieren in Workers nicht → Logik in das Worker-Skript übertragen
Umgebungsvariablen / Secrets Pages-Dashboard → [vars] in wrangler.jsonc oder wrangler secret put
# Aktuelle Wrangler-Version überprüfen
npx wrangler --version
# → Muss 4.34.0 oder höher sein, um das Limit von 100.000 Dateien zu unterstützen

# Aktuelle Konfiguration des Pages-Projekts prüfen
cat wrangler.jsonc

3. Kernmigration: Umstellung der wrangler.jsonc

Dies ist der minimale Diff-Vergleich, um von der bestehenden Pages-Konfiguration zu Workers Static Assets zu wechseln.

// wrangler.jsonc
{
  "name": "effidev",
  "compatibility_date": "2026-08-04",
- "pages_build_output_dir": "dist"
+ "main": "src/worker.ts",
+ "assets": {
+   "directory": "./dist",
+   "binding": "ASSETS"
+ }
}

Durch diese Änderung wird der Befehl für die Bereitstellung von wrangler pages deploy dist auf wrangler deploy umgestellt. Statische Assets werden automatisch aus dem Verzeichnis ./dist hochgeladen, während das Worker-Skript (src/worker.ts) die dynamische Logik verarbeitet.

Worker-Skript (src/worker.ts)

Dies ist der einfachste Einstiegspunkt für einen Worker, der ausschließlich statische Websites ausliefert.

// src/worker.ts
// Minimaler Worker zur ausschließlichen Auslieferung statischer Assets — ausreichend ohne dynamische Logik
export default {
  async fetch(
    request: Request,
    env: { ASSETS: Fetcher },
  ): Promise<Response> {
    // Alle Anfragen an die Bindung für statische Assets weiterleiten
    return env.ASSETS.fetch(request);
  },
};

VORSICHT FALLE: Wenn Sie assets.run_worker_first auf true setzen, durchläuft jede Anfrage das Worker-Skript und es fallen Gebühren für Worker-Aufrufe an. Für rein statische Websites sollte der Standardwert (false) beibehalten werden — in diesem Fall umgehen Anfragen für statische Assets den Worker und werden direkt und kostenlos bereitgestellt.

4. _headers / _redirects → Migration in das Worker-Skript

Die unter Pages verwendeten Dateien _headers und _redirects funktionieren in Workers nicht. Sie müssen direkt innerhalb des Worker-Skripts verarbeitet werden.

// src/worker.ts — Integrierte Version für Header und Weiterleitungen
export default {
  async fetch(
    request: Request,
    env: { ASSETS: Fetcher },
  ): Promise<Response> {
    const url = new URL(request.url);

    // _redirects-Ersatz: Alte URL-Muster mit 301 auf neue Pfade umleiten
    const redirects: Record<string, string> = {
      '/old-blog/': '/de/blog/',
      '/legacy-page/': '/de/',
    };

    const redirect = redirects[url.pathname];
    if (redirect) {
      return Response.redirect(new URL(redirect, request.url).toString(), 301);
    }

    // Statische Assets abrufen
    const response = await env.ASSETS.fetch(request);

    // _headers-Ersatz: Antwort-Header anpassen
    const headers = new Headers(response.headers);

    // Sicherheits-Header hinzufügen
    headers.set('X-Content-Type-Options', 'nosniff');
    headers.set('X-Frame-Options', 'DENY');
    headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');

    // Cache-Strategie: Bilder 1 Jahr, HTML 10 Minuten
    if (url.pathname.match(/\.(webp|png|jpg|svg|woff2)$/)) {
      headers.set('Cache-Control', 'public, max-age=31536000, immutable');
    } else if (url.pathname.endsWith('.html') || url.pathname.endsWith('/')) {
      headers.set('Cache-Control', 'public, max-age=600, s-maxage=3600');
    }

    return new Response(response.body, {
      status: response.status,
      headers,
    });
  },
};

In diesem Worker müssen Sie assets.run_worker_first auf true setzen, da Header manipuliert werden. Dadurch entstehen zwar Gebühren für Worker-Aufrufe, aber im Paid-Tarif sind 10 Millionen Anfragen pro Monat für 5 $ enthalten, was für die meisten statischen Websites völlig ausreicht.

// wrangler.jsonc — run_worker_first aktivieren
{
  "name": "effidev",
  "compatibility_date": "2026-08-04",
  "main": "src/worker.ts",
  "assets": {
    "directory": "./dist",
    "binding": "ASSETS",
    "run_worker_first": true
  }
}

5. SSR-Framework-Adapter-Konfiguration (Astro, Next.js, SvelteKit)

Eine statische Website wie dieser Blog, die Astro verwendet, benötigt keinen separaten Adapter — es reicht aus, die Build-Ausgabe einfach mit assets.directory zu verknüpfen. Wenn Sie jedoch ein Framework mit SSR nutzen, ist ein Cloudflare Workers-kompatibler Adapter erforderlich.

Astro (Statischer Build — am einfachsten)

# astro.config.mjs — Statischer Build ohne Adapter
# Bei output: 'static' (Standard) wird HTML direkt in dist/ generiert
npm run build
# → dist/-Ordner als assets.directory in wrangler.jsonc angeben

Astro (SSR-Modus)

npx astro add cloudflare
// astro.config.mjs — Cloudflare Workers SSR-Adapter
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  output: 'server',
  adapter: cloudflare({
    platformProxy: { enabled: true },
  }),
});

Next.js (opennextjs-cloudflare)

npx create-next-app@latest --typescript
npm install @opennextjs/cloudflare

Die Direktive "use cache" und RSC-Streaming, die im Leitfaden zu Next.js 16 "use cache" RSC-Streaming behandelt wurden, funktionieren über den Workers-Adapter ebenfalls einwandfrei.

6. Integrierte Bereitstellung von Durable Objects, Cron, D1 und R2

Dies ist die Kernfunktion, die unter Pages unmöglich war. Nach der Umstellung auf Workers Static Assets können Sie die statische Website sowie alle Backend-Bindungen in einer einzigen wrangler.jsonc verwalten.

// wrangler.jsonc — Integrierte Fullstack-Konfiguration
{
  "name": "effidev",
  "compatibility_date": "2026-08-04",
  "main": "src/worker.ts",
  "assets": {
    "directory": "./dist",
    "binding": "ASSETS",
    "run_worker_first": true
  },
  // D1-Datenbank
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "effidev-analytics",
      "database_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    }
  ],
  // R2 Object Storage
  "r2_buckets": [
    {
      "binding": "BUCKET",
      "bucket_name": "effidev-media"
    }
  ],
  // Cron Triggers (unter Pages nicht möglich!)
  "triggers": {
    "crons": ["0 */6 * * *"]
  }
}

Die R2-Bucket-Bindung, die im Leitfaden zur Migration von AWS S3 zu Cloudflare R2 beschrieben wurde, kann nun ohne separaten Worker direkt in den Blog-Worker integriert werden.

// src/worker.ts — Integrierter Handler für Cron + D1 + R2
interface Env {
  ASSETS: Fetcher;
  DB: D1Database;
  BUCKET: R2Bucket;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // API-Endpunkte werden direkt vom Worker verarbeitet
    if (url.pathname.startsWith('/api/')) {
      return handleApi(request, env);
    }

    // Der Rest wird als statisches Asset bereitgestellt
    return env.ASSETS.fetch(request);
  },

  // Cron, der alle 6 Stunden ausgeführt wird (unter Pages war ein separater Worker erforderlich!)
  async scheduled(
    _controller: ScheduledController,
    env: Env,
  ): Promise<void> {
    // Analysedaten in D1 aggregieren
    const result = await env.DB.prepare(
      'SELECT COUNT(*) as total FROM page_views WHERE date = date("now")',
    ).first();

    // Tagesbericht in R2 speichern
    await env.BUCKET.put(
      `reports/${new Date().toISOString().slice(0, 10)}.json`,
      JSON.stringify(result),
    );
  },
};

async function handleApi(request: Request, env: Env): Promise<Response> {
  // API-Logik implementieren
  return new Response(JSON.stringify({ status: 'ok' }), {
    headers: { 'Content-Type': 'application/json' },
  });
}

7. Änderung der Deployment-Befehle und DNS-Umstellung

Deployment-Befehl

# Vorher (Pages)
npx wrangler pages deploy dist --project-name=effidev

# Nachher (Workers Static Assets)
npx wrangler deploy

Workers-Deployments verwenden das name-Feld in wrangler.jsonc als Projektnamen.

DNS-Umstellung (Benutzerdefinierte Domain)

Benutzerdefinierte Domains von Pages-Projekten verweisen per CNAME auf <project>.pages.dev. Bei der Umstellung auf Workers müssen Sie die Domain im Cloudflare-Dashboard unter Workers → Routen (Routes) verbinden.

Schritt Aktion
1. Workers bereitstellen Worker + statische Assets mit wrangler deploy bereitstellen
2. Custom Domain verbinden Cloudflare-Dashboard → Workers → Betreffender Worker → Settings → Domains & Routes → Add Custom Domain
3. Pages Custom Domain entfernen Benutzerdefinierte Domain im Pages-Projekt entfernen (um DNS-Konflikte zu vermeiden)
4. Überprüfung curl -sI https://yourdomain.com/ | head -3 → Status 200 bestätigen

FALLE: Wenn Pages und Workers während der DNS-Umstellung gleichzeitig an dieselbe Domain gebunden sind, kommt es zu Routing-Konflikten. Entfernen Sie die Pages-Domain unbedingt zuerst, bevor Sie die Workers-Route hinzufügen.

8. Unnötige Dateien mit .assetsignore ausschließen

Wenn sich im Build-Ausgabeverzeichnis Dateien befinden, die nicht bereitgestellt werden sollen, verwenden Sie .assetsignore. Die Syntax entspricht der von .gitignore.

# dist/.assetsignore
_worker.js        # Überbleibsel aus dem Pages Advanced Mode — darf nicht als Worker-Asset hochgeladen werden
*.map             # Source Maps sollten nicht auf Production hochgeladen werden
.DS_Store

Häufig gestellte Fragen (FAQ)

Muss ich mein bestehendes Pages-Projekt sofort migrieren?

Nein. Bestehende Pages-Projekte funktionieren weiterhin. Eine Migration wird jedoch empfohlen, wenn Sie neue Funktionen wie Cron Triggers, direkte Durable Objects-Bindungen oder Workers Logs benötigen oder wenn Ihr Projekt mehr als 20.000 Dateien umfasst.

Fallen bei einer statischen Website Gebühren für Worker-Aufrufe an?

Wenn Sie assets.run_worker_first auf dem Standardwert (false) belassen, durchlaufen Anfragen für statische Assets den Worker nicht, sodass die Aufrufgebühren 0 betragen. Selbst wenn Sie den Wert aufgrund von Header-Anpassungen oder API-Logik auf true setzen, sind im Paid-Tarif (5 $/Monat) 10 Millionen Anfragen enthalten, sodass bei den meisten statischen Websites keine zusätzlichen Kosten entstehen.

Was passiert mit den Dateien _headers und _redirects?

Sie funktionieren unter Workers nicht. Sie müssen die Header-Einstellungen und Weiterleitungslogik direkt im Worker-Skript implementieren (siehe §4). Da dies über Code verwaltet wird, bietet es den Vorteil einer einfacheren Versionskontrolle und Testbarkeit.

Ich habe Pages Functions (/functions/) verwendet. Wie stelle ich um?

Integrieren Sie jede Datei aus Pages Functions in die Routing-Logik des Worker-Skripts. Bei Workers wird die Handler-Logik an einem einzigen Einstiegspunkt (main) basierend auf URL-Mustern verzweigt. Durch die Verwendung eines leichtgewichtigen Router-Frameworks wie Hono können Sie eine dateibasierte Routing-Struktur ähnlich der bestehenden Functions-Struktur beibehalten.