Deployment & Edge

Cloudflare Pages mit Claude Code:
Edge-Deployment 2026

Functions, D1 SQLite, KV Storage, R2 Object Storage, Wrangler — Claude Code deployt Full-Stack-Apps ans globale Edge-Netzwerk mit minimaler Latenz.

📅 6. Mai 2026 ⏱ 11 min Lesezeit 🔥 Cloudflare, Claude Code, TypeScript

Cloudflare Pages ist 2026 weit mehr als ein statisches Hosting. Mit Pages Functions, D1-Datenbanken, KV-Storage und R2 Object Storage lassen sich vollständige Full-Stack-Applikationen direkt an Cloudflares globalem Edge-Netzwerk mit über 310 Rechenzentren deployen. Die Latenzen liegen oft unter 20 ms — ohne teuren Origin-Server.

Claude Code verändert diesen Prozess fundamental: Statt mühsam Dokumentation zu lesen, Konfigurationsdateien von Hand zu schreiben und Deployment-Fehler zu debuggen, beschreibt man einfach, was man will — und Claude Code generiert wrangler.toml, TypeScript-Functions, Migrations-SQL und GitHub-Actions-Pipelines in Sekunden.

Dieser Artikel zeigt, wie Claude Code und Cloudflare Pages zusammen eine moderne Full-Stack-Deployment-Pipeline ergeben — von der ersten Konfiguration bis zu automatisierten CI/CD-Flows mit Custom Domains.

Edge-first

310+ PoPs weltweit, <20 ms Latenz, kein Origin-Server nötig

📊
D1 SQLite

Serverlose relationale Datenbank direkt am Edge, automatische Replikation

🔑
KV Storage

Global verteilter Key-Value-Store, <1 ms Read-Latenz nach Write

💾
R2 Storage

S3-kompatibles Object Storage ohne Egress-Kosten

1. Cloudflare Pages Grundlagen

Pages

Cloudflare Pages ist Cloudflares JAMstack-Hosting-Plattform, die seit 2024 vollständig mit dem Workers-Ökosystem zusammengewachsen ist. Jedes Deployment erzeugt automatisch eine Vorschau-URL, Git-Integrationen in GitHub und GitLab sind nativer Bestandteil — und mit Wrangler lässt sich alles auch über die CLI steuern.

Was Claude Code für den Setup tut:

wrangler.toml — Das Herzstück

Die wrangler.toml ist das zentrale Konfigurationsfile für jede Cloudflare Pages-App. Claude Code erstellt sie vollständig konfiguriert mit allen benötigten Ressourcen-Bindings:

wrangler.toml
# Cloudflare Pages Konfiguration — generiert von Claude Code name = "meine-app" compatibility_date = "2026-01-01" compatibility_flags = ["nodejs_compat"] pages_build_output_dir = "./dist" # D1 Datenbank-Binding [[d1_databases]] binding = "DB" database_name = "meine-app-db" database_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890" migrations_dir = "./migrations" # KV Namespace-Binding [[kv_namespaces]] binding = "CACHE" id = "abcdef1234567890abcdef1234567890" # R2 Bucket-Binding [[r2_buckets]] binding = "STORAGE" bucket_name = "meine-app-uploads" # Umgebungsvariablen [vars] APP_ENV = "production" API_VERSION = "v2" # Secrets (werden über wrangler secret put gesetzt) # AUTH_SECRET, OPENAI_API_KEY, etc. # Preview-Umgebung (Development/Staging) [env.preview] name = "meine-app-preview" [env.preview.vars] APP_ENV = "preview"

Erstes Deployment mit Wrangler

Das erste Deployment ist mit Claude Code ein Einzeiler-Prozess. Claude Code führt den Build durch, erstellt das Cloudflare-Projekt falls nötig und deployt direkt:

Terminal
# Wrangler installieren (falls noch nicht vorhanden) npm install -g wrangler@latest # Einloggen bei Cloudflare wrangler login # Projekt bauen (z.B. mit Vite, Next.js, Astro, etc.) npm run build # Erstes Deployment — Claude Code nutzt diesen Befehl npx wrangler pages deploy ./dist \ --project-name=meine-app \ --commit-message="Initial deployment via Claude Code" # Output: ✨ Uploading 47 files ✨ Deployment complete! 🌎 https://meine-app.pages.dev 🌎 https://a1b2c3.meine-app.pages.dev (Vorschau-URL)

Git-Integration & Preview URLs

Cloudflare Pages erstellt für jeden Branch und jeden Pull Request automatisch eine Preview-URL. Claude Code kann diese Architektur direkt über die Cloudflare API konfigurieren — Production-Branch ist main, alle anderen Branches erhalten isolierte Preview-Umgebungen.

Preview-URL-Schema:
Claude Code Tipp:

Claude Code merkt sich den State des Deployments und kann nach dem Deploy automatisch die Preview-URL aufrufen und mit Playwright testen, ob alle Endpoints korrekt antworten.

2. Pages Functions & Middleware

Functions

Pages Functions sind serverlose TypeScript/JavaScript-Funktionen, die direkt an Cloudflares Edge laufen — ohne Cold Starts, ohne Container, ohne Infrastruktur-Management. Das Routing ist dateibasiert im functions/-Verzeichnis.

Dateibasiertes Routing

Jede Datei im functions/-Verzeichnis wird zu einem API-Endpoint. Claude Code versteht dieses Routing-Schema und erstellt Functions mit korrekter TypeScript-Typisierung:

Routing-Struktur:
functions/api/users/[id].ts
import type { PagesFunction } from '@cloudflare/workers-types'; // Typdefinition für alle Bindings (generiert von Claude Code) interface Env { DB: D1Database; CACHE: KVNamespace; STORAGE: R2Bucket; AUTH_SECRET: string; } // GET /api/users/:id — Einzelnen User aus D1 laden export const onRequestGet: PagesFunction<Env> = async (context) => { const { id } = context.params; const { DB, CACHE } = context.env; // Cache-First: KV prüfen bevor D1 Query const cached = await CACHE.get(`user:${id}`, { type: 'json' }); if (cached) { return Response.json({ data: cached, source: 'cache' }); } // D1 Query const stmt = DB.prepare('SELECT id, name, email, created_at FROM users WHERE id = ?'); const user = await stmt.bind(id).first(); if (!user) { return Response.json({ error: 'User nicht gefunden' }, { status: 404 }); } // In KV cachen (TTL: 5 Minuten) await CACHE.put(`user:${id}`, JSON.stringify(user), { expirationTtl: 300 }); return Response.json({ data: user, source: 'db' }); }; // PUT /api/users/:id — User updaten export const onRequestPut: PagesFunction<Env> = async (context) => { const { id } = context.params; const body = await context.request.json<{ name?: string; email?: string }>(); if (!body.name && !body.email) { return Response.json({ error: 'Keine Änderungen übermittelt' }, { status: 400 }); } await context.env.DB .prepare('UPDATE users SET name = COALESCE(?, name), email = COALESCE(?, email) WHERE id = ?') .bind(body.name ?? null, body.email ?? null, id) .run(); // Cache invalidieren nach Update await context.env.CACHE.delete(`user:${id}`); return Response.json({ success: true, updated: id }); }; // DELETE /api/users/:id — User löschen export const onRequestDelete: PagesFunction<Env> = async (context) => { const { id } = context.params; const authHeader = context.request.headers.get('Authorization'); // Auth-Check via Secret if (authHeader !== `Bearer ${context.env.AUTH_SECRET}`) { return Response.json({ error: 'Nicht autorisiert' }, { status: 401 }); } await context.env.DB.prepare('DELETE FROM users WHERE id = ?').bind(id).run(); await context.env.CACHE.delete(`user:${id}`); return Response.json({ success: true, deleted: id }); };

Middleware-Pattern

Middleware in Pages Functions läuft vor allen anderen Functions. Claude Code erstellt Middleware-Dateien für Authentication, Logging, Rate Limiting und CORS automatisch:

functions/_middleware.ts
import type { PagesFunction } from '@cloudflare/workers-types'; import type { Env } from './types'; // CORS-Middleware — läuft vor allen API-Endpunkten const corsMiddleware: PagesFunction<Env> = async (context) => { const response = await context.next(); const headers = new Headers(response.headers); headers.set('Access-Control-Allow-Origin', 'https://meine-app.com'); headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); return new Response(response.body, { ...response, headers }); }; // Rate-Limiting-Middleware via KV const rateLimitMiddleware: PagesFunction<Env> = async (context) => { const ip = context.request.headers.get('CF-Connecting-IP') ?? 'unknown'; const key = `ratelimit:${ip}`; const limit = 100; // Requests pro Minute const current = await context.env.CACHE.get(key); const count = current ? parseInt(current) : 0; if (count >= limit) { return Response.json( { error: 'Rate limit überschritten', retryAfter: 60 }, { status: 429, headers: { 'Retry-After': '60' } } ); } await context.env.CACHE.put(key, String(count + 1), { expirationTtl: 60 }); return context.next(); }; // Middleware-Kette exportieren export const onRequest = [corsMiddleware, rateLimitMiddleware];
Wichtig:

Middleware im functions/-Root läuft für alle Routes. Unterordner können eigene _middleware.ts haben, die nur für diesen Unterbereich gilt — zum Beispiel Auth nur für /api/admin/*.

3. D1 Datenbank

D1 SQLite

D1 ist Cloudflares serverlose SQLite-Datenbank, die direkt am Edge läuft. Seit dem GA-Release 2025 unterstützt D1 read replicas in allen Regionen, sodass Reads immer vom nächstgelegenen PoP beantwortet werden.

D1 Datenbank erstellen und verbinden

Terminal
# D1 Datenbank erstellen wrangler d1 create meine-app-db # Output enthält die database_id für wrangler.toml: ✅ Successfully created DB 'meine-app-db' in region WEUR Created your new D1 database. [[d1_databases]] binding = "DB" database_name = "meine-app-db" database_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890" # SQL direkt ausführen (für Tests) wrangler d1 execute meine-app-db --command="SELECT sqlite_version()" # Migration aus Datei ausführen wrangler d1 execute meine-app-db --file=./migrations/0001_initial.sql # Migrations mit wrangler d1 migrations (automatisches Tracking) wrangler d1 migrations apply meine-app-db

SQL-Migrationen

Claude Code erstellt SQL-Migrationen vollständig typsicher und mit allen nötigen Indizes. Das Migrations-System von D1 trackt, welche Migrationen bereits angewandt wurden:

migrations/0001_initial.sql
-- Migration 0001: Initiales Schema — generiert von Claude Code -- D1 nutzt SQLite-Syntax mit Cloudflare-Erweiterungen CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))), name TEXT NOT NULL, email TEXT NOT NULL UNIQUE, plan TEXT NOT NULL DEFAULT 'free' CHECK (plan IN ('free', 'pro', 'enterprise')), created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_users_email ON users(email); CREATE INDEX idx_users_plan ON users(plan); CREATE TABLE IF NOT EXISTS sessions ( id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))), user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, token_hash TEXT NOT NULL UNIQUE, expires_at DATETIME NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_sessions_user_id ON sessions(user_id); CREATE INDEX idx_sessions_token ON sessions(token_hash); -- Trigger: updated_at automatisch setzen CREATE TRIGGER update_users_timestamp AFTER UPDATE ON users FOR EACH ROW BEGIN UPDATE users SET updated_at = CURRENT_TIMESTAMP WHERE id = OLD.id; END;

D1 mit Drizzle ORM

Für typsichere Datenbankzugriffe empfiehlt Claude Code Drizzle ORM, das nativen D1-Support bietet:

src/db/schema.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'; import { sql } from 'drizzle-orm'; // Schema-Definition — 1:1 mit Claude Code Migrations abgeglichen export const users = sqliteTable('users', { id: text('id').primaryKey(), name: text('name').notNull(), email: text('email').notNull().unique(), plan: text('plan', { enum: ['free', 'pro', 'enterprise'] }).default('free').notNull(), createdAt: text('created_at').default(sql`CURRENT_TIMESTAMP`), updatedAt: text('updated_at').default(sql`CURRENT_TIMESTAMP`), }); export type User = typeof users.$inferSelect; export type NewUser = typeof users.$inferInsert;
functions/api/users.ts (mit Drizzle)
import { drizzle } from 'drizzle-orm/d1'; import { users } from '../../src/db/schema'; import { eq, desc } from 'drizzle-orm'; import type { PagesFunction } from '@cloudflare/workers-types'; import type { Env } from './types'; export const onRequestGet: PagesFunction<Env> = async (context) => { const db = drizzle(context.env.DB); const url = new URL(context.request.url); const limit = parseInt(url.searchParams.get('limit') ?? '20'); const offset = parseInt(url.searchParams.get('offset') ?? '0'); const result = await db .select({ id: users.id, name: users.name, email: users.email, plan: users.plan }) .from(users) .orderBy(desc(users.createdAt)) .limit(limit) .offset(offset); return Response.json({ data: result, limit, offset }); };
D1 Performance-Tipp:

Batch-Queries mit db.batch() sparen Round-Trips. Für komplexe Reports nutze prepare().bind().all() und kombiniere das mit KV-Caching für häufige Aggregationen.

4. KV Storage

KV

Cloudflare KV ist ein global verteilter Key-Value-Store. Writes propagieren innerhalb von ~60 Sekunden, Reads sind immer lokal am Edge (<1 ms). KV eignet sich für Caching, Session-Storage, Feature-Flags und häufig gelesene Konfiguration.

KV Namespace erstellen

Terminal
# KV Namespace für Production erstellen wrangler kv namespace create CACHE # Output für wrangler.toml: ✅ Successfully created namespace Add the following to your wrangler.toml: [[kv_namespaces]] binding = "CACHE" id = "abcdef1234567890abcdef1234567890" # Preview-Namespace für lokale Entwicklung wrangler kv namespace create CACHE --preview # Werte direkt über CLI setzen (für Tests) wrangler kv key put "feature:new-dashboard" "true" --namespace-id=abcdef... wrangler kv key get "feature:new-dashboard" --namespace-id=abcdef... # Alle Keys auflisten wrangler kv key list --namespace-id=abcdef...

KV API in Functions

Claude Code generiert KV-Zugriffe mit korrektem Typing und optimierten Mustern für Caching und Session-Management:

src/lib/cache.ts
// Cache-Abstraktionsschicht — generiert von Claude Code export class EdgeCache { constructor(private kv: KVNamespace) {} // Typsicheres get mit automatischem JSON-Parsing async get<T>(key: string): Promise<T | null> { return await this.kv.get<T>(key, { type: 'json' }); } // put mit optionalem TTL und automatischem JSON-Serializing async put<T>(key: string, value: T, ttlSeconds?: number): Promise<void> { const opts = ttlSeconds ? { expirationTtl: ttlSeconds } : undefined; await this.kv.put(key, JSON.stringify(value), opts); } // delete — einzelner Key async delete(key: string): Promise<void> { await this.kv.delete(key); } // Pattern-basierte Invalidierung (prefix) async invalidatePrefix(prefix: string): Promise<number> { const list = await this.kv.list({ prefix }); const deletes = list.keys.map(k => this.kv.delete(k.name)); await Promise.all(deletes); return list.keys.length; } // Cache-or-Fetch-Pattern (stale-while-revalidate) async getOrSet<T>( key: string, fetcher: () => Promise<T>, ttlSeconds: number = 300 ): Promise<T> { const cached = await this.get<T>(key); if (cached !== null) return cached; const fresh = await fetcher(); await this.put(key, fresh, ttlSeconds); return fresh; } } // Session-Store via KV export class SessionStore { constructor(private kv: KVNamespace) {} async create(userId: string, sessionData: Record<string, unknown>): Promise<string> { const sessionId = crypto.randomUUID(); await this.kv.put( `session:${sessionId}`, JSON.stringify({ userId, ...sessionData, createdAt: Date.now() }), { expirationTtl: 86400 } // 24 Stunden ); return sessionId; } async get(sessionId: string) { return await this.kv.get(`session:${sessionId}`, { type: 'json' }); } async destroy(sessionId: string): Promise<void> { await this.kv.delete(`session:${sessionId}`); } }
KV Use Case TTL Empfehlung Pattern
API Response Cache 60–300s api:endpoint:params
User Session 86400s (24h) session:uuid
Rate Limit Counter 60s ratelimit:ip:minute
Feature Flags kein TTL feature:flag-name
DB Query Results 300–3600s query:hash

5. R2 Object Storage

R2

Cloudflare R2 ist S3-kompatibles Object Storage ohne Egress-Gebühren. Für Upload-heavy Apps (User-Avatare, Dokumente, Media-Files) ist R2 oft günstiger als S3 + CloudFront. Claude Code erstellt vollständige Upload-Pipelines mit Presigned URLs und direktem Browser-Upload.

R2 Bucket erstellen

Terminal
# Bucket erstellen wrangler r2 bucket create meine-app-uploads # Public Bucket (für öffentliche Assets) wrangler r2 bucket create meine-app-assets --public # Für S3-API-Kompatibilität: Access Key erstellen wrangler r2 access-key create --bucket meine-app-uploads # Datei hochladen (für Tests) wrangler r2 object put meine-app-uploads/test.txt --file ./test.txt # Objekt-Liste wrangler r2 object list meine-app-uploads

R2 Upload-Pipeline mit Presigned URLs

Das sichere Pattern für Browser-Uploads: Browser fragt nach Presigned URL, lädt direkt zu R2 hoch — kein Traffic über Functions, keine Datei-Limits durch den Worker:

functions/api/upload/presign.ts
import type { PagesFunction } from '@cloudflare/workers-types'; import type { Env } from '../types'; // Erlaubte Dateitypen und Maximalgröße const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf']; const MAX_SIZE_MB = 10; export const onRequestPost: PagesFunction<Env> = async (context) => { const { filename, contentType, size } = await context.request.json<{ filename: string; contentType: string; size: number; }>(); // Validierung if (!ALLOWED_TYPES.includes(contentType)) { return Response.json({ error: 'Dateityp nicht erlaubt' }, { status: 400 }); } if (size > MAX_SIZE_MB * 1024 * 1024) { return Response.json({ error: `Max. ${MAX_SIZE_MB}MB erlaubt` }, { status: 413 }); } // Einzigartigen Storage-Key generieren const ext = filename.split('.').pop() ?? 'bin'; const key = `uploads/${Date.now()}-${crypto.randomUUID()}.${ext}`; // Presigned URL erzeugen (gültig für 15 Minuten) const presigned = await context.env.STORAGE.createMultipartUpload(key, { httpMetadata: { contentType }, customMetadata: { originalName: filename, uploadedAt: new Date().toISOString() }, }); // Alternative: R2 S3-API für echte Presigned URLs return Response.json({ key, uploadId: presigned.uploadId, publicUrl: `https://assets.meine-app.com/${key}`, expiresIn: 900, }); }; // GET: Objekt-Metadaten abrufen export const onRequestGet: PagesFunction<Env> = async (context) => { const key = new URL(context.request.url).searchParams.get('key'); if (!key) return Response.json({ error: 'key fehlt' }, { status: 400 }); const obj = await context.env.STORAGE.head(key); if (!obj) return Response.json({ error: 'Datei nicht gefunden' }, { status: 404 }); return Response.json({ key, size: obj.size, contentType: obj.httpMetadata?.contentType, uploaded: obj.uploaded, customMetadata: obj.customMetadata, }); };

R2 als CDN Proxy (Serve-Funktion)

functions/assets/[...path].ts
// R2-Objekte über Pages Functions ausliefern (mit Cache-Control) export const onRequestGet: PagesFunction<Env> = async (context) => { const path = (context.params.path as string[]).join('/'); const obj = await context.env.STORAGE.get(path); if (!obj) { return new Response('Not Found', { status: 404 }); } const headers = new Headers(); obj.writeHttpMetadata(headers); headers.set('Cache-Control', 'public, max-age=31536000, immutable'); headers.set('ETag', obj.httpEtag); // Conditional Request Support const ifNoneMatch = context.request.headers.get('If-None-Match'); if (ifNoneMatch === obj.httpEtag) { return new Response(null, { status: 304, headers }); } return new Response(obj.body, { headers }); };

6. Wrangler & CI/CD

CI/CD

Cloudflare Pages CI/CD-Integration ist mit Wrangler und GitHub Actions vollautomatisiert. Claude Code erstellt komplette Pipeline-Dateien, die bei jedem Push auf main deployen und bei Pull Requests Preview-URLs erzeugen.

Wrangler Environments

Mit Environments in wrangler.toml lassen sich Production, Staging und Preview-Umgebungen mit unterschiedlichen Bindings und Variablen konfigurieren:

wrangler.toml (mit Environments)
# Globale Konfiguration (Production-Default) name = "meine-app" compatibility_date = "2026-01-01" pages_build_output_dir = "./dist" [[d1_databases]] binding = "DB" database_name = "meine-app-db-prod" database_id = "prod-database-id" [vars] APP_ENV = "production" LOG_LEVEL = "warn" API_URL = "https://api.meine-app.com" # Staging-Umgebung [env.staging] name = "meine-app-staging" [[env.staging.d1_databases]] binding = "DB" database_name = "meine-app-db-staging" database_id = "staging-database-id" [env.staging.vars] APP_ENV = "staging" LOG_LEVEL = "debug" API_URL = "https://staging-api.meine-app.com" # Preview-Umgebung (für lokale Entwicklung) [env.preview] name = "meine-app-preview" [[env.preview.kv_namespaces]] binding = "CACHE" id = "preview-kv-namespace-id" preview_id = "preview-kv-namespace-id"

GitHub Actions Pipeline

Claude Code erstellt eine vollständige GitHub Actions Workflow-Datei mit Build, Test und Deployment in einem Schritt:

.github/workflows/deploy.yml
name: Deploy to Cloudflare Pages on: push: branches: [main] pull_request: branches: [main] jobs: test-and-deploy: runs-on: ubuntu-latest name: Build, Test & Deploy steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js 22 uses: actions/setup-node@v4 with: node-version: '22' cache: 'npm' - name: Install dependencies run: npm ci - name: TypeScript type check run: npx tsc --noEmit - name: Run unit tests run: npm test - name: Build application run: npm run build env: VITE_APP_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }} - name: D1 Migrations (nur Production) if: github.ref == 'refs/heads/main' run: npx wrangler d1 migrations apply meine-app-db --env production env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - name: Deploy to Cloudflare Pages uses: cloudflare/wrangler-action@v3 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} command: pages deploy ./dist --project-name=meine-app - name: Comment Preview URL (bei PRs) if: github.event_name == 'pull_request' uses: actions/github-script@v7 with: script: | github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: '🚀 Preview deployed! Check Cloudflare Pages for the URL.' })

Custom Domains & Headers

Custom Domains werden über das Cloudflare Dashboard oder die API verknüpft. Mit einer _headers-Datei im Build-Output setzt Claude Code Security-Header:

public/_headers
# Cloudflare Pages Headers — generiert von Claude Code # Gilt für alle Seiten /* X-Frame-Options: DENY X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), microphone=(), geolocation=() Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.meine-app.com # Assets: lange Cache-Time, immutable /assets/* Cache-Control: public, max-age=31536000, immutable # API: kein Caching /api/* Cache-Control: no-store, no-cache, must-revalidate
Vollständiger Claude Code Workflow:
  1. Claude Code analysiert die bestehende App-Architektur
  2. Generiert wrangler.toml mit allen Bindings für D1, KV, R2
  3. Erstellt TypeScript-Types für alle Environments
  4. Schreibt Pages Functions mit vollständiger Fehlerbehandlung
  5. Erstellt SQL-Migrations und Drizzle-Schema
  6. Generiert GitHub Actions Workflow
  7. Führt erstes Deployment aus und verifiziert alle Endpoints
Secrets sicher verwalten:

wrangler secret put AUTH_SECRET — Secrets werden verschlüsselt gespeichert und sind NUR in der Runtime verfügbar, niemals in wrangler.toml oder Logs. Claude Code erinnert dich daran, alle Secrets vor dem ersten Deployment zu setzen.

Lokale Entwicklung mit wrangler dev

Terminal
# Lokaler Dev-Server mit echten Edge-Bindings wrangler pages dev ./dist \ --d1=DB:meine-app-db \ --kv=CACHE:preview-kv-id \ --r2=STORAGE:meine-app-uploads \ --port=8788 # Oder via npm script (empfohlen von Claude Code): # package.json: "dev": "vite build --watch & wrangler pages dev ./dist" npm run dev # D1-Tabellen lokal abfragen wrangler d1 execute meine-app-db --local --command="SELECT COUNT(*) FROM users" # KV-Werte lokal setzen wrangler kv key put "feature:beta" "true" --binding=CACHE --local
Claude Code + Wrangler dev:

Claude Code startet wrangler pages dev automatisch, führt Changes sofort durch, testet alle API-Endpoints und gibt direkt Feedback — ohne manuelles Terminal-Wechseln.

Edge-Deployment mit Claude Code ausprobieren

Deploye deine nächste Full-Stack-App mit Cloudflare Pages und Claude Code — D1, KV, R2 inklusive, in Minuten produktionsreif.

Jetzt kostenlos starten →
🤖
SpockyMagicAI Redaktion Praxiswissen zu AI-Agents, Edge-Deployment und moderner Entwicklungsautomatisierung — direkt aus dem Produktionseinsatz.