How to migrate from Fastly to Vercel with zero downtime

Consolidate your CDN infrastructure on Vercel to reduce latency, simplify your configuration, and improve your developer experience without interrupting service to your users.

Michael Merrill
9 min read
Last updated January 29, 2026

Running Fastly in front of Vercel adds cost and complexity that most teams no longer need. This dual-CDN setup, often inherited from legacy configurations or custom caching requirements, creates redundant infrastructure that's harder to operate and slower to serve.

If you've been running this setup for more than a year, you've probably felt the friction: debugging issues across two systems, explaining the architecture to new hires, wondering why you're paying for two CDNs. This guide is for teams ready to simplify.

Moving to Vercel's Global Network brings caching and routing closer to the application, which means fewer moving parts and less to debug.

This guide provides a systematic approach to translating your Fastly VCL (Varnish Configuration Language) logic into Vercel configurations. We will explore:

  • Why consolidate: Business and technical benefits of a unified platform
  • Mental models: Key differences between traditional VCL-based CDNs and Vercel
  • Technical translation: Mapping your Fastly configuration and architecture to Vercel
  • The migration: Executing a zero-downtime DNS cutover
  • Post-migration: Optimizing for the Vercel Edge

By consolidating on Vercel's Global Network, your CDN logic, security rules, and caching strategies can be version-controlled and deployed atomically alongside your application code.

AspectBenefit
PerformanceEliminates an extra network hop between CDNs, reducing latency. Global cache purges complete in under 300ms.
CostEliminates dual-CDN infrastructure costs and reduces operational overhead.
WorkflowCDN configuration lives in your repo, type-checked at build time, and testable in preview deployments before reaching production.
SecurityFull traffic visibility allows Vercel Firewall to block threats before they reach your compute.
ObservabilityUnified logging from edge to function. No more correlating logs across two systems.
Developer ExperienceDeploying your app updates the CDN automatically. No separate config pushes or manual cache purges.

Benchmarks and SLA figures above typically apply to Enterprise plan tiers, though the architectural benefits remain consistent across all tiers.

Fastly and Vercel are both programmable CDNs, but Fastly expects you to configure CDN behavior directly in VCL. Vercel can infer it from your application code through framework conventions, or you can configure it explicitly using vercel.json or vercel.ts. See the project configuration docs.

  • Configuration model: Fastly requires you to define CDN behavior explicitly in VCL or edge programs. Vercel infers it from your application code and framework conventions. Your caching, routing, and headers live alongside the code they affect.
  • Deployment model: Fastly separates application deploys from CDN configuration and cache management. Vercel deploys are atomic: shipping new code updates the CDN and gives you a fresh cache namespace automatically.

You shift from operating a CDN to describing application intent. On Vercel, the framework becomes the interface to the infrastructure.

Before touching production traffic, translate your Fastly patterns into Vercel-native configurations.

To successfully migrate from Fastly, you must understand where your logic lives. Vercel moves beyond the standalone "config file" by allowing behavior to be defined at the Global, Middleware, and Route levels.

LevelPrimary ToolBest For...
Global Config (static)vercel.jsonSimple, hardcoded rules: redirects, headers, and rewrites that don't change between environments.
Global Config (programmatic)vercel.tsBuild-time rules that need environment variables, secrets via deploymentEnv(), or dynamically generated routes. Type-safe helpers catch errors before deploy.
Request Logicmiddleware.ts (or proxy.ts in Next.js 16+)Request-time execution at the Edge: Auth checks, Geo-IP routing, and A/B testing before the cache. Comparable to Cloudflare Workers or Fastly Compute.
Route LogicApplication CodeCaching (ISR), Status Codes, and Content-Type. This replaces vcl_fetch by defining how specific pages behave.
Security RulesVercel Firewall / WAFIP blocking, rate limiting, geo restrictions, bot protection, and custom security rules. Replaces Fastly ACLs and edge security logic.

Next.js 16 renamed middleware.ts to proxy.ts. The examples in this guide use both names. Use whichever matches your Next.js version.

Use the Feature Map to find the Vercel equivalent of each Fastly feature you're using.

Fastly ConceptVercel EquivalentTarget PrimitiveImplementation Method
Edge DictionariesEdge Configmiddleware.tsGlobal, low-latency data stores read by Middleware for flags and redirects.
Surrogate-KeysCache-TagApplication CodeUse for precise, on-demand global cache invalidation via framework APIs.
ShieldingGlobal NetworkPlatform (Native)Automatic; Vercel's multi-tier cache acts as a native global shield by default.
VCL SubroutinesMiddlewaremiddleware.tsUse middleware.ts (or proxy.ts) for request-time logic like Geo-IP or Auth.
beresp.gracestale-while-revalidatevercel.ts / AppControlled via standard Cache-Control headers in your configuration or code.
Remote Log StreamingLog DrainsIntegrationsNear real-time integrations with Datadog, Splunk, and New Relic.

Response headers can be applied globally or to specific paths using vercel.ts. This approach replaces custom Varnish subroutines with typed helpers that are easier to reason about and maintain.

set resp.http.X-Content-Type-Options = "nosniff";
set resp.http.X-Frame-Options = "DENY";
import { routes, type VercelConfig } from '@vercel/config/v1';
export const config: VercelConfig = {
headers: [
routes.header('/(.*)', [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' }
])
]
};

When proxying to a backend, you often need to inject secret keys. vercel.ts allows you to use deploymentEnv to pull these from your project secrets during the build.

if (req.url.path ~ "^/api/") {
set req.backend = api_backend;
set req.http.Authorization = "Bearer " + "SECRET_TOKEN";
}
import { routes, deploymentEnv, type VercelConfig } from '@vercel/config/v1';
export const config: VercelConfig = {
rewrites: [
routes.rewrite('/api/:path*', '<https://api.example.com/:path*>', {
requestHeaders: {
authorization: `Bearer ${deploymentEnv('API_TOKEN')}`
}
})
]
};

In Fastly, redirects are often handled by triggering a synthetic error. In Vercel, redirects are built into the configuration layer.

if (req.url.path == "/old-path") {
error 801 "/new-path";
}
import { routes, type VercelConfig } from '@vercel/config/v1';
export const config: VercelConfig = {
redirects: [
routes.redirect('/old-path', '/new-path', { permanent: true })
]
};

For migrations involving thousands of path-to-path redirects, Vercel's Bulk Redirects feature handles up to 1M redirects per project and is more efficient than listing them in vercel.ts. See the docs for configuration options and limits.

For logic that must execute at runtime, such as Geo-IP redirects or A/B testing (which often requires complex vcl_recv logic), use middleware.ts.

if (client.geo.country_code == "GB") {
set req.http.X-Regional-Path = "/uk";
}
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
const { nextUrl: url, headers } = request;
// Replicating Geo-IP Logic (Fastly client.geo)
const country = headers.get('x-vercel-ip-country') || 'US';
if (country === 'GB' && !url.pathname.startsWith('/uk')) {
return NextResponse.redirect(new URL(`/uk${url.pathname}`, request.url));
}
return NextResponse.next();
}

Fastly uses beresp.ttl. Vercel uses the standard Cache-Control header, but the vercel.ts helper routes.cacheControl simplifies the syntax.

if (req.url.path ~ "\\.(png|jpg)$") {
set beresp.ttl = 3600s;
}
import { routes, type VercelConfig } from '@vercel/config/v1';
export const config: VercelConfig = {
headers: [
routes.cacheControl('/:path*.(png|jpg)', {
public: true,
maxAge: '1 hour'
})
]
};

Vercel caches more aggressively by default than Fastly, and ties cache keys to deployment IDs. This means most manual purge workflows disappear: deploying new code gives you a fresh cache.

  • Static assets: Framework-generated assets (JS, CSS, fonts, images produced by your build) are automatically cached globally with long-lived, immutable headers. Files in the public folder are not cached by default unless you explicitly set Cache-Control headers via vercel.ts or your framework. This is a common point of confusion when migrating from CDNs that cache all static files by default.
  • Static pages (SSG): Build-time generated pages are cached globally and tied to the deployment that produced them.
  • ISR pages: Incremental Static Regeneration pages are cached globally and background-revalidated on your interval. Generated pages persist across deployments and enable instant rollbacks.

Because cache keys include the deployment ID, shipping a new deployment gives you a fresh cache namespace. In most cases, you do not need to purge like you would with Fastly.

Dynamic routes (API routes and SSR HTML) are not cached by default. You opt in using ISR or Cache-Control headers. For page-like content, ISR is the closest replacement for Fastly HTML caching and TTL logic. ISR has built-in stale-while-revalidate behavior.

// app/products/[id]/page.tsx
export const revalidate = 3600; // Replaces `set beresp.ttl = 3600s`
export default function Page({ params }) {
return <h1>Product {params.id}</h1>;
}

For API routes or non-page responses, use headers:

export async function GET() {
return Response.json({ ok: true }, {
headers: {
'Cache-Control': 's-maxage=60, stale-while-revalidate=300',
},
});
}

Use stale-while-revalidate to keep serving cached content when your origin is slow or unavailable, replacing Fastly’s beresp.grace pattern.

When you do need to update cached content, Vercel supports tag- and path-based invalidation. Invalidate serves stale content while revalidating in the background and is the recommended default. For Next.js, prefer revalidatePath() and revalidateTag() over manual purges.

See the Next.js documentation for revalidatePath and revalidateTag.

Once your logic is translated and your caching strategy is aligned, you are ready for the production move.

Before changing DNS, ensure Vercel behaves the same as your current Fastly setup.

  • Recreate redirects, rewrites, headers, and caching using vercel.ts or vercel.json.
  • Move request-time logic (geo, auth, experiments) into Middleware using middleware.ts or proxy.ts.
  • Replicate IP rules, rate limits, and bot protections using Vercel Firewall and WAF.

Reserved Paths: The /.well-known path is reserved on Vercel and cannot be redirected or rewritten. Ensure any existing Fastly logic for this path is removed or handled natively. This catches people off guard. If you have ACME (Automatic Certificate Management Environment) challenges (for SSL/TLS certificate validation) or other logic on /.well-known, plan for it early.

Set up the production hostname on Vercel while Fastly continues to serve traffic. Follow the zero-downtime domain migration guide for detailed steps on adding your domain, verifying ownership, and provisioning TLS.

Confirm real-world behavior without impacting users.

  • Add a test domain such as test.example.com to the same project and point it to Vercel.
  • Ensure production environment variables and secrets are configured.
  • Validate redirects, headers, caching (x-vercel-cache), and critical routes end to end.
  • Trigger Middleware functions (like Geo-IP redirects) to confirm they execute correctly on the Global Network.

Compare performance between Fastly and Vercel

Before cutting over, measure the latency difference between your current Fastly-fronted setup and Vercel directly. This gives you a baseline and helps justify the migration.

  1. Identify your Vercel deployment URL (e.g., your-project-abc123.vercel.app).
  2. Run synthetic tests against both endpoints from multiple regions:
    1. Your production URL (Fastly in front): https://example.com
    2. Your Vercel deployment URL directly: https://your-project-abc123.vercel.app
  1. Use tools like [WebPageTest](https://www.webpagetest.org/), curl with timing, or your APM provider to compare TTFB, full page load, and cache hit rates.
curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s\n" https://example.com
curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s\n" https://your-project-abc123.vercel.app

Document the delta. Most teams see measurable improvement from eliminating the extra hop, but having numbers makes the case internally and gives you a regression baseline post-migration.

Move traffic with a single, reversible change.

  1. 24-48 hours before cutover, lower your DNS TTL for the production record to 60 seconds. (Some registrars enforce a minimum of 300 seconds, so check yours beforehand.)
  2. Update your production DNS record:
    • Subdomains: CNAME to cname.vercel-dns.com
    • Apex domains: use the Vercel-provided root record
  3. Monitor error rates, cache behavior, and middleware execution.

Once traffic is stable:

  • Restore your DNS TTL to its previous value.
  • Decommission Fastly only after confirming correct behavior.

If unexpected issues occur after cutover, you can revert immediately:

  1. Revert DNS records to point back to your Fastly service
  2. Wait 60 seconds (your lowered TTL) for your changes to propagate globally
  3. Verify traffic has returned to Fastly via your monitoring tools
  4. Investigate issues in Vercel logs before attempting another cutover

Once traffic is stable, monitor for one week before moving on to the next step. The first week after migration is when you'll catch misconfigurations that didn't surface in testing. After that, focus on tuning.

Most teams see the biggest gains from caching improvements and image optimization. Start there before tuning function configuration.

  • Enable Speed Insights to track Core Web Vitals and field performance trends.
  • Review TTFB and address slow routes by improving caching, rendering strategy, or backend proximity.
  • Ensure Image Optimization is enabled and used consistently for shipped images.

If you had WAF rules in Fastly, don't assume they migrated automatically. Rebuild them explicitly in Vercel's firewall before considering the migration complete.

The goal is to catch issues before your users report them. Observability investments pay off most in the first month when you're still learning the new system's failure modes.

Migrating from Fastly to Vercel means shifting from operating a CDN to describing what your application needs. The payoff isn't just fewer systems to manage. It's that your CDN behavior becomes testable, reviewable, and deployable like any other code change. You'll know the migration is complete when your team stops thinking about the CDN as a separate layer and starts treating it as part of the application.

Was this helpful?

supported.
How to migrate from Fastly to Vercel with zero downtime | Vercel Knowledge Base