Comprehensive CDN Migration Checklist: Every Step from Planning to Cutover

I was on a bridge call at 1:47 AM with an e-commerce team whose entire checkout flow went blank the moment they flipped their DNS. They'd done a perfect CDN migration on paper, but no one had catalogued the one legacy API route that relied on a custom header the old provider injected. That outage cost them six hours and a lot of revenue. A solid CDN migration isn't about the cutover itself, it's about the weeks of migration planning you do before you ever touch a CNAME. This migration checklist is the process I've refined over a decade of moves, big and small.

Audit What You Actually Have, Not What You Think You Have

Every CDN migration I've seen get delayed starts the same way: the team assumes they know their current setup. Then we find the marketing site loading fonts from a separate bucket that was never added to the CDN, or the mobile app hardcoding an old edge hostname from three vendors ago. You can't build a new delivery layer until you've mapped the old one completely. I spend the first week shadowing developers, SREs, and even marketing folks to ask, "Where does this content actually come from and who can change it?" You'll be surprised how many answers aren't in any diagram.

For beginners, this inventory is the most important part of your migration checklist. List every domain and subdomain behind the CDN, every origin (yes, including that forgotten staging origin someone kept for testing), all cache behaviors, edge rules, redirects, header manipulations, WAF rules, and authentication logic. Don't forget TLS certificates, their expiry dates, and who owns certificate rotation. I also pull 30 days of logs and billing data to see real traffic patterns, not just what the architecture doc says. If you haven't run a formal evaluation before, use a framework like the CDN Vendor Evaluation Matrix: Scoring Providers on What Actually Matters to make sure you capture pricing, performance, and security needs alongside the technical inventory. That list becomes your single source of truth for everything else.

One thing that saves me every time is testing cache behavior directly from the edge before I trust anyone's documentation. The HTTP Working Group Specifications define how Cache-Control, Age, and CDN-Cache-Control should work, but every provider implements them a little differently. I run simple header checks against each important content type to see what's really cacheable.

# Verify how your current CDN handles different content types
# Run this for your top 10 URLs before you migrate

curl -s -D - https://www.example.com/static/app.9c3f2.js -o /dev/null | grep -iE "cache-control|age|x-cache|cdn-cache"

curl -s -D - https://www.example.com/api/user/profile -o /dev/null | grep -iE "cache-control|age|x-cache|cdn-cache"

curl -s -D - https://www.example.com/images/hero-banner.jpg -o /dev/null | grep -iE "cache-control|age|x-cache|cdn-cache"

# Log the results with origin vs. edge timing
curl -w "TTFB: %{time_starttransfer}s | Total: %{time_total}s\n" -o /dev/null -s https://www.example.com/

Translate Requirements Into a Testable Configuration

Once you know what you have, you have to decide what you need the new CDN to do, and write it down in a way a vendor can actually build. I learned this after a migration in 2017 where we wrote "needs fast image optimization" in our requirements doc. Every vendor said yes, and none of them did it the way our app expected. Now I write requirements as testable statements: "Must support on-the-fly WebP/AVIF conversion via URL parameter, with origin image max size of 10MB and 95th percentile conversion time under 80ms." If you're starting from scratch, the CDN RFP Template: Writing Requirements That Get Meaningful Vendor Responses will help you avoid those vague asks that lead to bad proposals.

This is also where a proof of concept pays for itself. Don't just let a vendor run a synthetic demo on their own domain. Set up the new CDN in front of a cloned origin or a low-traffic staging property and replay real traffic. I've caught breaking changes in query string sorting, cookie forwarding, and case-sensitive header handling during PoCs that would have been disasters during the CDN cutover. A good plan for this is outlined in CDN Proof of Concept Planning: Designing a PoC That Answers Real Questions - it walks through traffic shadowing and dark-launch techniques that don't risk your users. Your goal isn't to see if the new CDN is fast in a lab, it's to see if it's compatible with your application in the real world.

Keep your configuration in code from day one, even if the new provider has a nice dashboard. It makes peer review possible and gives you a rollback artifact. Here's a stripped-down Terraform example I use to document every behavior so nothing lives only in someone's clicks:

# cdn-config.tf - Keep your CDN migration configuration versioned
resource "cdn_distribution" "primary" {
  origin {
    hostname = "origin.example.com"
    protocol = "https"
    port     = 443
  }

  behavior {
    path_pattern     = "/static/*"
    cache_ttl        = 86400
    compress         = true
    forwarding {
      query_strings = false
      cookies       = "none"
      headers       = ["Accept", "Accept-Encoding"]
    }
  }

  behavior {
    path_pattern     = "/api/*"
    cache_ttl        = 0  # Do not cache authenticated API
    forwarding {
      query_strings = true
      cookies       = "all"
      headers       = ["Authorization", "Content-Type"]
    }
  }

  # All redirects and header rules must be explicit here
  custom_error_response {
    error_code = 404
    response_code = 404
    response_page = "/errors/404.html"
  }
}

Set Baselines and Define What "Successful" Means Before the Move

If you don't know your current numbers, you can't tell if the new CDN is better or worse. About two weeks before any planned cutover, I freeze changes on the old CDN and start measuring. I pull the same metrics from the same places every day so I have a reliable baseline to compare against. Don't rely on the vendor's dashboard alone. Collect data from your real users, your origin logs, and synthetic monitors in the regions that matter to you. The guidance on web.dev Articles for Core Web Vitals and real-user monitoring is a good reference for what to track on the client side.

I've made the mistake of declaring a migration "done" because the error rate looked fine, while Largest Contentful Paint had regressed by 600ms for users in Southeast Asia. That's why I now force the team to agree on pass/fail thresholds before we schedule the cutover. Pick three to five metrics that map to business outcomes, not vanity stats. Everyone - engineering, product, and support - should sign off on them. If any metric breaches its threshold for more than 15 minutes after the switch, we roll back. No debate on the call.

The table below is the exact template I use with beginner teams. Fill in your current baseline over a full business cycle, including a peak traffic day. Your targets shouldn't be aspirational guesses; they should be "at least as good as today" plus any specific improvement you were promised during the proof of concept.

Metric to Track Current Baseline (7-Day Avg) Target After Migration How to Measure
Time to First Byte (p95, Global) 185 ms ≤ 190 ms Synthetic monitoring from 5 regions + RUM
Edge Cache Hit Ratio 91.4% ≥ 90.0% CDN access logs / provider dashboard
Origin Offload (Bandwidth %) 88% ≥ 87% Origin egress vs. edge egress (bytes)
HTTP 5xx Error Rate 0.12% ≤ 0.15% Edge logs aggregated per minute
Largest Contentful Paint (p75, Mobile) 2.3s ≤ 2.4s Real User Monitoring (CrUX / web-vitals.js)

Validate With a Production Pilot, Not Just a Staging Check

Staging environments lie. They don't have real user geography, they don't have your actual bot traffic mix, and they definitely don't replicate that one critical header your origin sets only for iOS 17. We've seen migrations pass every synthetic test in staging and then immediately tank cache hit ratio by 30% in production because no one accounted for query string variance. You can't simulate this accurately — you have to pilot it live.

A proper pilot means serving real production traffic through the new CDN configuration on your actual production hostname, but limited in scope. Don't just CNAME a test subdomain. Use weighted DNS, a load balancer split, or your existing CDN's traffic steering to shift 5-10% of real user traffic to the new provider for at least 72 to 96 hours — long enough to capture a full weekday/weekend cycle and a cache fill. You're looking for parity, not perfection. Your new cache hit ratio should be within 2-3% of baseline, origin offload should match, p95 TTFB shouldn't degrade more than 15-20ms, and your 5xx error rate needs to stay under 0.05%. We've got a full framework for what to measure and how long to run it in our CDN Pilot Testing Methodology: Metrics, Duration, and Success Criteria.

Automate the comparison or you'll miss it. Don't eyeball dashboards. Pull access logs from both CDNs into the same warehouse and diff them hourly. Check for header normalization issues — like one CDN lowercasing Cache-Control directives or stripping Vary — that break downstream caching. Verify image optimization, Brotli vs. Gzip negotiation, and especially purge and cache invalidation. If your pilot can't purge a single URL globally in under 5 seconds consistently, you don't want to find that out during an incident.

#!/bin/bash
# pilot-diff.sh - Compare cache behavior: legacy vs. new CDN
TARGET_HOST="www.example.com"
URLS=("/" "/api/products?id=123" "/static/app.9c4a2.js")

for path in "${URLS[@]}"; do
  echo "=== $path ==="
  for edge in "legacy.cdn.example.net" "new.cdn.example.net"; do
    echo "-- $edge --"
    curl -s -D - -o /dev/null \
      -H "Host: $TARGET_HOST" \
      --resolve "$TARGET_HOST:443:$(dig +short $edge | head -n1)" \
      "https://$TARGET_HOST$path" | grep -iE '^(HTTP|X-Cache|Age|Cache-Control|Content-Encoding)'
  done
  echo ""
done

# Fail if hit ratio diverges >3% from baseline after 1k requests
# hook this into your pilot metrics job

Executing the Cutover and Troubleshooting the Inevitable Surprises

The cutover itself shouldn't be an event — it should be a non-event. If you're doing a "big bang" DNS flip at midnight, you've already added risk you didn't need. Drop your DNS TTLs to 60 seconds at least 48 hours before migration (and verify your authoritative DNS actually honors it — some providers floor at 300s). Keep both CDNs active and warm for seven days after cutover. The cleanest pattern we've run at scale is a weighted shift: 10% -> 50% -> 100% over 4-6 hours with automated health checks at each stage. If you're not sure how to sequence that, our CDN Traffic Cutover Strategy: Migrating Without Downtime or Data Loss breaks down the zero-downtime playbook, including how to handle long-lived WebSocket and video connections that won't survive a DNS switch.

Most post-cutover issues aren't outages, they're subtle degradations. The top three we see: First, cache key mismatches. Your new CDN might be ignoring a query parameter you *need* to vary on (like ?currency=EUR) or, more commonly, caching on one you shouldn't (like ?utm_source), which fragments your cache and kills hit ratio. Second, WAF/WAF rule parity. Your old CDN's managed ruleset won't map 1:1. Expect a spike in false positives — monitor your WAF block rate and have a 15-minute rule to put the new WAF in log-only mode if block rate jumps more than 3x. Third, and most painful, is DNS itself. If you're also switching DNS providers as part of this, you can't just copy records. You need to account for apex CNAME flattening, DNSSEC chain breaks, and EDNS Client Subnet handling. We cover that separately in our CDN DNS Migration Planning: Changing DNS Providers Without Breaking Resolution.

Define your rollback triggers *before* you need them, and make them automatic. Don't rely on someone watching a dashboard at 2 a.m. to decide. We'll set alerts like: if origin 5xx rate exceeds 1% for 3 minutes, or p95 latency degrades by 100ms for 5 minutes, or cache hit ratio drops below 75% when baseline is 92% — automatically shift DNS weight back to the legacy CDN. And test the rollback. On day two of your pilot, do a full rehearsal: shift 100% of pilot traffic back in under 60 seconds and confirm logs and monitoring flip cleanly. If you can't roll back cleanly, you can't cut over safely.

Frequently Asked Questions

How long should we keep the old CDN contract active after cutover?

Keep it fully active for at least 30 days, and don't let anyone tell you it's wasted spend. You'll need it for instant rollback during the first week, and for at least 2-3 weeks you'll still see traffic hitting it from ISP resolvers that ignored your 60s TTL and from hardcoded IPs. We've measured 1-4% of traffic still arriving at the old edge 14 days after a "full" DNS cutover. Decommission only after your logs show near-zero requests for 72 hours straight.

Do we need to warm the new CDN cache before shifting traffic?

You don't need to pre-warm everything, but you must warm the top 5-10% of your hottest objects. For a typical e-commerce site, that's maybe 1,000-5,000 URLs that drive 80% of requests. Use a script to request them through the new CDN with a cache-bypass bypass disabled, or better, let your pilot fill cache naturally for 24 hours and then promote to 50%. If you cut 100% cold, expect origin load to spike 4-6x in the first 15 minutes and potentially tip your origin over.

What's the most common reason CDN migrations fail validation?

It's almost never performance. It's header and cache-control logic. Specifically, Vary, Cache-Control: private/no-store, and Set-Cookie handling. One CDN might, by default, not cache anything with a Set-Cookie response, while another does unless you explicitly tell it not to. That one difference can double your origin requests or, worse, serve private cached content to the wrong user. Audit every origin response that sets a cookie.

Can we migrate CDN and DNS provider at the same time?

You can, but you shouldn't make it a single atomic change. Decouple them. Migrate DNS first, stabilize for a week, then migrate the CDN. If you must do both together, use a DNS provider that supports secondary DNS and keep both providers authoritative during the transition. Changing your edge network and your resolution path simultaneously doubles your blast radius and makes root-cause analysis miserable when something breaks.

Related Articles

References & Standards: HTTP Working Group Specifications · web.dev Articles · IETF Datatracker