Platform

Migrating a Next.js Site from Vercel to Cloudflare: Blob to R2, Build to Wrangler

How to move compute and storage with fewer surprises: Blob to R2, platform build to Wrangler, and a rollout plan that protects reliability.

Jack Ridgway··11 min read
Moving from Vercel to Cloudflare is rarely only a hosting change. You are changing runtime assumptions, storage primitives, and deployment tooling. Done well, the move can lower costs and improve edge distribution. Done quickly, it can break uploads, caching, and preview workflows.

Why Teams Make This Move

Most migrations start with one of three pressures: cost predictability, tighter control over edge behavior, or a desire to consolidate services. Cloudflare can be a strong fit when your traffic profile is global and you want to manage caching and storage policy more directly.

The trade-off is that you move from a highly integrated product surface to a more explicit platform model. That is not a downside if your team is comfortable owning infrastructure decisions.

Migration Map Before You Touch Production

  • Inventory every Vercel capability your app currently depends on
  • Identify all data paths currently writing to Vercel Blob
  • Define a cutover plan with clear rollback points
  • Replicate analytics and monitoring first, not last

Vercel to Cloudflare Capability Mapping

Vercel Functions -> Cloudflare Workers
Vercel Blob      -> Cloudflare R2
Vercel Edge      -> Cloudflare edge network + cache rules
Vercel deploy    -> Wrangler deployment pipeline

Step 1: Move Blob Storage to R2

Treat storage migration as a separate project. The biggest mistakes happen when compute and storage are migrated in one rushed deployment.

Create and Bind an R2 Bucket

# Create bucket
wrangler r2 bucket create site-assets-prod

# Add binding in wrangler.toml
[[r2_buckets]]
binding = "ASSETS"
bucket_name = "site-assets-prod"

Migrate Existing Objects

Export objects from Blob and import to R2 with deterministic keys. Keep object URLs stable where possible to avoid SEO and cache churn.

# Example pseudo workflow
node scripts/export-blob-manifest.js > blob-manifest.json
node scripts/sync-blob-to-r2.js --manifest blob-manifest.json

Replace Storage Client in Next.js

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const client = new S3Client({
  region: "auto",
  endpoint: process.env.R2_ENDPOINT,
  credentials: {
    accessKeyId: process.env.R2_ACCESS_KEY_ID!,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
  },
});

await client.send(new PutObjectCommand({
  Bucket: process.env.R2_BUCKET,
  Key: key,
  Body: fileBuffer,
  ContentType: mimeType,
}));

Step 2: Deploy Next.js with Wrangler

The goal is deterministic builds and predictable deploys. Keep your app behavior stable first, optimize runtime details second.

Install and Configure

npm install --save-dev wrangler

# Authenticate
wrangler login
name = "jackridgway-site"
compatibility_date = "2026-03-31"

[vars]
NODE_ENV = "production"

[[r2_buckets]]
binding = "ASSETS"
bucket_name = "site-assets-prod"

Deploy Command in CI

npm ci
npm run build
npx wrangler deploy

Step 3: Handle Caching Intentionally

Vercel abstracted many caching defaults for you. On Cloudflare, be explicit about HTML vs static asset policy.

  • Static assets: long TTL and immutable cache keys
  • HTML: short TTL with safe revalidation strategy
  • Uploads: versioned object keys to avoid stale cache collisions

Rollout Strategy That Minimizes Risk

  1. Run both platforms in parallel in pre-production
  2. Mirror a subset of production traffic to validate behavior
  3. Cut over DNS with a low TTL window
  4. Keep rollback runbook ready for 24 to 48 hours

Common Pitfalls

  • Forgetting environment variable parity across pipelines
  • Breaking signed URL behavior during Blob to R2 migration
  • Assuming cache behavior is equivalent out of the box
  • Shipping migration and feature work in the same release

Final Thought

A successful Vercel to Cloudflare migration is not about swapping one deployment command for another. It is about making runtime behavior, storage boundaries, and operational ownership explicit. Teams that treat this as a platform design exercise, not only a hosting change, usually finish with better reliability and clearer cost control.