$ ls ./menu

© 2025 ESSA MAMDANI

cd ../blog
6 min read
Web Development

Beyond the Vercel Tax: A Technical Guide to Migrating to Cloudflare Pages

> Scaling past Vercel's bandwidth limits? This comprehensive guide walks you through migrating your stack to Cloudflare Pages and Workers for better performance and predictable pricing.

Audio version coming soon
Beyond the Vercel Tax: A Technical Guide to Migrating to Cloudflare Pages
Verified by Essa Mamdani

I’ve spent years building on Vercel. Their Developer Experience (DX) is, frankly, the gold standard. But as your traffic scales and your architecture matures, many of us hit a wall—usually a financial one or a technical one. Whether it’s the dreaded "bandwidth tax" or the desire for true edge-native execution without the cold-start overhead of traditional serverless functions, Cloudflare is the logical next step.

Migrating isn't just about changing a deployment target; it’s about shifting your mental model from "Serverless Functions" to "Edge Isolates." Let’s dive into the architecture, the code, and the pro-level optimizations required to move your stack from Vercel to Cloudflare.

Why Make the Move?

Before we touch the code, let's talk shop. Vercel is a layer on top of AWS (mostly). When you pay Vercel, you’re paying for that abstraction. Cloudflare is the infrastructure.

  1. The Economics: Cloudflare’s egress pricing is legendary (as in, it’s mostly non-existent). If you’re pushing terabytes of data, Vercel will send you a bill that looks like a mortgage payment.
  2. The Runtime: Vercel uses AWS Lambda under the hood for many regions. Cloudflare uses V8 Isolates. This means zero cold starts and a global distribution by default, not as an "add-on."
  3. The Ecosystem: With D1 (SQL), R2 (Object Storage), and KV (Key-Value), you’re moving into a holistic ecosystem where the database sits inches away from the compute.

Phase 1: Auditing Your Framework

If you are running a static site (Vite, Hugo, Astro), the migration is a "five-minute job." If you are running Next.js, we need to talk.

Vercel and Next.js are siblings. Vercel implements several Next.js features (like ISR and Image Optimization) using proprietary infrastructure. To run Next.js on Cloudflare, you need the @cloudflare/next-on-pages adapter.

Pro Tip: The Edge Runtime

Before migrating, ensure your application is compatible with the edge runtime. Cloudflare Workers do not run a full Node.js environment; they run a V8 isolate. This means no fs module and no net module. If your dependencies rely on heavy Node.js internals, you’ll need to find alternatives or polyfills.

Phase 2: Configuring the Environment

In Vercel, you likely managed environment variables via their dashboard. In the Cloudflare ecosystem, we use wrangler.toml. This file is your source of truth for your application’s configuration.

Create a wrangler.toml in your root directory:

toml
1name = "my-awesome-app"
2compatibility_date = "2024-05-01"
3pages_build_output_dir = ".vercel/output/static" # If using next-on-pages
4
5[vars]
6API_URL = "https://api.essamamdani.com"
7NODE_VERSION = "20"
8
9[[kv_namespaces]]
10binding = "CACHE_BUCKET"
11id = "your-namespace-id"
12
13[[r2_buckets]]
14binding = "ASSETS"
15bucket_name = "my-app-assets"

Phase 3: The Build Process (Next.js Example)

If you're migrating a Next.js app, you'll need to install the adapter and modify your build command.

bash
1npm install --save-dev @cloudflare/next-on-pages

Update your package.json build script:

json
1{
2  "scripts": {
3    "build": "next build",
4    "pages:build": "npx @cloudflare/next-on-pages"
5  }
6}

In your code, you must explicitly opt-in to the edge runtime for your routes:

typescript
1// app/api/hello/route.ts
2export const runtime = 'edge';
3
4export async function GET() {
5  return new Response(JSON.stringify({ message: "Hello from the Edge!" }), {
6    headers: { "Content-Type": "application/json" },
7  });
8}

Phase 4: Handling Data and Storage

One of the biggest hurdles in migrating is the data layer. If you were using Vercel KV or Postgres, you’re likely using Upstash or Neon under the hood. You can keep using them, but for maximum performance, you should migrate to Cloudflare’s native primitives.

From Vercel Blob to Cloudflare R2

Cloudflare R2 is S3-compatible and has zero egress fees. If you have an upload utility, you’ll swap the Vercel SDK for the standard S3 client or the Wrangler binding.

typescript
1// Accessing R2 via binding in a Cloudflare Worker
2export default {
3  async fetch(request, env) {
4    const object = await env.ASSETS.get('my-file.png');
5    return new Response(object.body);
6  }
7}

From Vercel Postgres to Cloudflare D1

D1 is Cloudflare’s native SQLite-based database. It’s incredibly fast for read-heavy workloads. If your app is heavily reliant on complex PostgreSQL features, you might want to stick with a hosted Postgres provider (like Neon) and connect via the Cloudflare Hyperdrive to pool connections and reduce latency.

Phase 5: Routing and DNS

Vercel handles DNS well, but Cloudflare is the world's DNS.

  1. Transfer the Domain: You don't have to transfer the registrar, but you must point your Nameservers to Cloudflare.
  2. Page Rules & Transform Rules: If you had complex redirects in your vercel.json, you should implement these using Cloudflare "Bulk Redirects" or "Snippets" (the new way to run lightweight logic without a full Worker).

Phase 6: The CI/CD Pipeline

Vercel’s git integration is seamless. Cloudflare Pages offers a very similar experience. Connect your GitHub/GitLab repository to the Cloudflare dashboard, and it will auto-deploy on every push.

However, for elite setups, I recommend using GitHub Actions to deploy via the Wrangler CLI. This gives you more control over the build environment and allows for automated integration testing before the code hits the edge.

yaml
1# .github/workflows/deploy.yml
2name: Deploy to Cloudflare Pages
3on: [push]
4
5jobs:
6  deploy:
7    runs-on: ubuntu-latest
8    steps:
9      - uses: actions/checkout@v4
10      - name: Install dependencies
11        run: npm ci
12      - name: Build
13        run: npm run pages:build
14      - name: Publish to Cloudflare
15        uses: cloudflare/wrangler-action@v3
16        with:
17          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
18          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
19          command: pages deploy .vercel/output/static --project-name=my-app

Pro Tips for the Elite Developer

1. Local Development with Miniflare

One thing developers miss about Vercel is vercel dev. Cloudflare provides npx wrangler dev, which uses Miniflare. It simulates the entire Cloudflare environment (KV, D1, R2) locally. Use it to ensure your edge-specific code works before it’s deployed.

2. Smart Placement

Cloudflare recently introduced "Smart Placement." If your Worker needs to talk to a database in us-east-1, Cloudflare can automatically detect this and run your Worker closer to the database rather than the user to minimize the "round-trip" latency. Turn this on in your dashboard.

3. Logpush and Observability

Vercel's logs are easy to read but hard to export without a Pro plan. Cloudflare Logpush allows you to stream your logs directly to an S3 bucket, Datadog, or New Relic. For high-traffic apps, this is non-negotiable for debugging.

Final Thoughts

The migration from Vercel to Cloudflare is a rite of passage for growing applications. You are moving from a platform that prioritizes "Developer Ease" to one that prioritizes "Architectural Power."

By following this guide, you’ve not only saved your budget from scaling costs, but you’ve also placed your application on the fastest network on the planet. The initial friction of adapting to the V8 isolate runtime pays dividends in the form of sub-10ms response times and a stack that can handle anything the internet throws at it.

Go forth and build at the edge. If you hit a snag, you know where to find me.