---
title: How to migrate from Fastly to Vercel with zero downtime
description: Consolidate your CDN infrastructure on Vercel to reduce latency, simplify your configuration, and improve your developer experience without interrupting service to your users.
url: /kb/guide/how-to-migrate-from-fastly-to-vercel-with-zero-downtime
canonical_url: "https://vercel.com/kb/guide/how-to-migrate-from-fastly-to-vercel-with-zero-downtime"
last_updated: 2026-07-16
authors: Michael Merrill
related:
  - /docs/project-configuration
  - /docs/edge-config
  - /docs/data-cache
  - /docs/cdn
  - /docs/routing-middleware
  - /docs/drains
  - /docs/redirects/bulk-redirects
  - /docs/incremental-static-regeneration
  - /kb/guide/zero-downtime-migration
  - /docs/speed-insights
  - /docs/speed-insights/metrics
  - /docs/image-optimization
  - /docs/headers/security-headers
  - /docs/vercel-firewall/vercel-waf
  - /docs/vercel-firewall/vercel-waf/managed-rulesets
  - /docs/vercel-firewall/vercel-waf/custom-rules
  - /docs/deployment-protection
  - /docs/observability/observability-plus
  - /docs/headers/cache-control-headers
  - /docs/functions/configuring-functions/region
  - /docs/cdn-cache
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

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.

## What you will learn

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
  

## Why consolidate

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.

| **Aspect**               | **Benefit**                                                                                                                       |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| **Performance**          | Eliminates an extra network hop between CDNs, reducing latency. Global cache purges complete in under 300ms.                      |
| **Cost**                 | Eliminates dual-CDN infrastructure costs and reduces operational overhead.                                                        |
| **Workflow**             | CDN configuration lives in your repo, type-checked at build time, and testable in preview deployments before reaching production. |
| **Security**             | Full traffic visibility allows Vercel Firewall to block threats before they reach your compute.                                   |
| **Observability**        | Unified logging from edge to function. No more correlating logs across two systems.                                               |
| **Developer Experience** | Deploying 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._

## Mental models

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](https://vercel.com/docs/project-configuration).

### Key differences to understand

- **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.

## Technical translation

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

### Vercel’s configuration primitives

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.

| Level                        | Primary Tool                                   | Best For...                                                                                                                                                     |
| ---------------------------- | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Global Config (static)       | `vercel.json`                                  | Simple, hardcoded rules: redirects, headers, and rewrites that don't change between environments.                                                               |
| Global Config (programmatic) | `vercel.ts`                                    | Build-time rules that need environment variables, secrets via `deploymentEnv()`, or dynamically generated routes. Type-safe helpers catch errors before deploy. |
| Request Logic                | `middleware.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 Logic                  | Application Code                               | Caching (ISR), Status Codes, and Content-Type. This replaces `vcl_fetch` by defining how specific pages behave.                                                 |
| Security Rules               | Vercel Firewall / WAF                          | IP 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.

### Fastly to Vercel feature map

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

| **Fastly Concept**       | **Vercel Equivalent**                                                      | **Target Primitive**  | **Implementation Method**                                                       |
| ------------------------ | -------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------- |
| **Edge Dictionaries**    | [**Edge Config**](https://vercel.com/docs/edge-config)                     | `middleware.ts`       | Global, low-latency data stores read by Middleware for flags and redirects.     |
| **Surrogate-Keys**       | [**Cache-Tag**](https://vercel.com/docs/data-cache#tag-based-revalidation) | **Application Code**  | Use for precise, on-demand global cache invalidation via framework APIs.        |
| **Shielding**            | [**Global Network**](https://vercel.com/docs/cdn)                          | **Platform (Native)** | Automatic; Vercel's multi-tier cache acts as a native global shield by default. |
| **VCL Subroutines**      | [**Middleware**](https://vercel.com/docs/routing-middleware)               | `middleware.ts`       | Use `middleware.ts` (or `proxy.ts`) for request-time logic like Geo-IP or Auth. |
| **beresp.grace**         | `stale-while-revalidate`                                                   | `vercel.ts` / **App** | Controlled via standard `Cache-Control` headers in your configuration or code.  |
| **Remote Log Streaming** | [**Log Drains**](https://vercel.com/docs/drains)                           | **Integrations**      | Near real-time integrations with Datadog, Splunk, and New Relic.                |

### Code-to-code comparison

#### Headers

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.Access-Control-Allow-Origin = "*"; set resp.http.Access-Control-Allow-Methods = "GET, POST, OPTIONS"; set resp.http.Access-Control-Allow-Headers = "Content-Type, Authorization";`

`import { routes, type VercelConfig } from '@vercel/config/v1'; export const config: VercelConfig = { headers: [routes.header('/(.*)', [ { key: 'Access-Control-Allow-Origin', value: '*' }, { key: 'Access-Control-Allow-Methods', value: 'GET, POST, OPTIONS' }, { key: 'Access-Control-Allow-Headers', value: 'Content-Type, Authorization' }, ]) ] };` #### Rewrites 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')}` } }) ] };`` #### Redirects 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 }) ] };` #### Bulk redirects For migrations involving thousands of path-to-path redirects, Vercel's [Bulk Redirects](https://vercel.com/docs/redirects/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.

#### Routing middleware

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 { geolocation } from '@vercel/functions' import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export function middleware(request: NextRequest) { const { country } = geolocation(request) if (country === 'GB' && !request.nextUrl.pathname.startsWith('/uk')) { return NextResponse.redirect( new URL(`/uk${request.nextUrl.pathname}`, request.url) ) } return NextResponse.next() };``

#### Caching

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' }) ] };` ### Caching strategies 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. #### What Vercel caches by default - **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](https://vercel.com/docs/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 content, SSR, and ISR

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. #### Purging and revalidation 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](https://nextjs.org/docs/app/api-reference/functions/revalidatePath) and [revalidateTag](https://nextjs.org/docs/app/api-reference/functions/revalidateTag).

## The migration

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

### 1\. Prepare Vercel to match production behavior

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.

### 2\. Add the production domain and provision TLS

Set up the production hostname on Vercel while Fastly continues to serve traffic. Follow the [zero-downtime domain migration guide](https://vercel.com/kb/guide/zero-downtime-migration) for detailed steps on adding your domain, verifying ownership, and provisioning TLS.

### 3\. Validate with a test domain

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.

- Identify your Vercel deployment URL (e.g., `your-project-abc123.vercel.app`).
  
- Run synthetic tests against both endpoints from multiple regions:
  
  - Your production URL (Fastly in front): `https://example.com`
    
  - Your Vercel deployment URL directly: `https://your-project-abc123.vercel.app`
    

> ⚠️ This must be a production deployment, not a preview URL. Vercel only deploys middleware to all global edge regions for production deployments. Preview deployments run middleware only in your project's configured function regions. Testing against a preview URL will show artificially higher latency and won't reflect real-world performance.

- Use tools like \[WebPageTest\]([https://www.webpagetest.org/](https://www.webpagetest.org/)), `curl` with timing, or your APM provider to compare TTFB, full page load, and cache hit rates.
  

`# Fastly-fronted production URL curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s\n" https://example.com # Vercel production deployment (must be production, not a preview URL) 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.

### 4\. Execute the DNS cutover

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.
   

### 5\. Stabilize or roll back

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
   

## Post-migration

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.

### Performance

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

- Enable [Speed Insights](https://vercel.com/docs/speed-insights) to track Core Web Vitals and field performance trends.
  
- Review [TTFB](https://vercel.com/docs/speed-insights/metrics#time-to-first-byte-ttfb) and address slow routes by improving caching, rendering strategy, or backend proximity.
  
- Ensure [Image Optimization](https://vercel.com/docs/image-optimization) is enabled and used consistently for shipped images.
  

### Security

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.

- Implement [security headers and CSP](https://vercel.com/docs/headers/security-headers) for baseline hardening.
  
- Configure [Vercel WAF](https://vercel.com/docs/vercel-firewall/vercel-waf) with [managed rulesets](https://vercel.com/docs/vercel-firewall/vercel-waf/managed-rulesets) and add [custom rules](https://vercel.com/docs/vercel-firewall/vercel-waf/custom-rules) where needed.
  
- Enable [Deployment Protection](https://vercel.com/docs/deployment-protection) to prevent unauthorized access to previews and non-production deployments.
  

### Reliability

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.

- Enable [Observability Plus](https://vercel.com/docs/observability/observability-plus) to investigate errors, traffic patterns, and regressions quickly.
  
- Implement [caching headers](https://vercel.com/docs/headers/cache-control-headers) for static assets and function responses to reduce origin dependency and smooth spikes.
  
- If eligible, enable [automatic Function failover](https://vercel.com/docs/functions/configuring-functions/region#automatic-failover) for multi-region redundancy.
  

## Conclusion

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.

## Next Steps

- [Explore Vercel caching documentation](https://vercel.com/docs/cdn-cache)
  
- [Learn more about Middleware](https://vercel.com/docs/routing-middleware)
  
- [Configure Vercel WAF](https://vercel.com/docs/vercel-firewall/vercel-waf)