Case studies · Tutorial 11
Migrate a Static Site from Vercel to Cloudflare Pages
Move a static production site through a preview deployment, canonical-domain cutover, redirect audit, cache check, and rollback-safe verification.

0 of 12 complete
Source-checked and updated: August 10, 2026. Pricing, limits, redirect behavior, cache controls, and network figures were checked against the linked Cloudflare and Vercel documentation.
By the end of this tutorial, you will have a preview deployment, a written domain and cache plan, a tested production cutover, and a rollback path you can use before DNS or search traffic becomes a problem.
We moved two of our production sites, DogDogHK and Boxmining, from Vercel to Cloudflare Pages.
The short version is simple. Both sites are built as static files, Cloudflare Pages serves static asset requests for free without a page-view ceiling, and the sites felt faster after the move. Cloudflare also gives us more of the network controls and diagnostic information we actually want when something is wrong.
The longer version is less flattering. Cloudflare’s dashboard is awful. Pages feels buried inside a much larger infrastructure product. The first deployment is easy; getting DNS, the canonical apex domain, the www redirect and cache behavior exactly right took several iterations.
I would still make the same decision again.
These were the right sites to move
DogDogHK is a bilingual directory for dog-friendly restaurants, parks and malls in Hong Kong. Boxmining is a long-running crypto and AI publication with a large article archive. They look very different, but their hosting needs are similar:
- most visitors receive prebuilt HTML, CSS, JavaScript, fonts and images;
- the sites can rebuild when content changes instead of rendering every page on demand;
- neither site needs a server process for an ordinary page view;
- traffic can be uneven because a guide, airdrop or local recommendation can suddenly become popular.
That last point matters. Static publishing removes a lot of operational risk, but only if the host’s pricing model rewards static architecture.
Cloudflare’s current Pages pricing documentation says requests for static assets are free and unlimited on both free and paid plans. There is an important boundary: if a request invokes a Pages Function, it is billed and limited as a Workers request. “Unlimited” is not a magic label for any application deployed to Pages. It is a very good deal for files that stay files.
The Free plan still has practical limits. Cloudflare currently allows 500 builds per month, 20,000 files per site, a 25 MiB maximum per asset and 100 Pages projects per account. Those limits fit these sites. A large video file, server-rendered application or function-heavy product would need a different calculation.

Why Vercel made me nervous
Vercel is easier to like at first. The project dashboard is clearer, Git deployments feel polished and framework integration is excellent. If I were teaching someone to deploy their first modern frontend, Vercel’s interface would be the easier lesson.
The problem is what happens after a commercial site grows.
Vercel’s Hobby plan is free, but it is restricted to personal, non-commercial use. The current Pro plan costs $20 per month and includes $20 of usage credit. Its published allowance includes 10 million edge requests and 1 TB of fast data transfer per billing cycle. After that, Vercel lists on-demand pricing that starts at $2 per million edge requests and $0.15 per GB, with regional variation.
Every request to a static page or asset counts as an edge request, and sending the response counts as fast data transfer. Caching can prevent function execution, but Vercel’s own usage example confirms that a cached static page still consumes those two metered resources.
This does not mean one bot attack automatically creates a giant bill. Vercel says automatic DDoS mitigation is included on every plan and traffic it blocks as a DDoS attack is not charged. Hobby users are not charged for overages either; the project can be paused when it exceeds its limits.
The uncomfortable case is legitimate traffic. A link goes viral, a search ranking lands, or a large audience downloads image-heavy pages. On Pro, that success can become usage-based cost. Vercel has improved its controls: new teams receive a default on-demand budget, and Spend Management can notify you, call a webhook or pause projects at a hard limit. But that still leaves an owner choosing between an open-ended bill and taking the site offline at the moment it is most useful.
For a static publication, I do not want that trade.
Cloudflare has the network shape we wanted
Cloudflare and Vercel both operate global delivery networks. “Cloudflare is always faster” would be too broad. Performance depends on the visitor, the asset, cache state, page weight and how each deployment is configured.
Our experience on these two sites was that Cloudflare Pages was faster, especially once the cache rules were tuned. We no longer have a clean, like-for-like copy of the old production deployments, so I am not going to invent a before-and-after benchmark.
There is a sound infrastructure reason for our preference. Cloudflare currently publishes a network presence in 335 cities across more than 125 countries, with direct connections to 13,000 networks. Vercel publishes 126 points of presence across 51 countries, backed by 20 compute-capable regions. Those are not perfectly identical units, but Cloudflare’s disclosed footprint is substantially broader.
That matters to us because our audience is not concentrated in one US region. DogDogHK is Hong Kong-first. Boxmining has readers across Asia, Europe and North America. Cloudflare also exposes the machinery more directly: DNS records, proxy state, cache status, redirect rules, security events and per-request CF-Ray location codes all live in the same system.
In a live check from Hong Kong on August 10, both apex homepages were served by Cloudflare through its Singapore edge. The www versions returned permanent redirects to the apex domains. Boxmining’s hashed font asset returned CF-Cache-Status: HIT with a one-year immutable cache policy. The HTML remained revalidatable. That is the split we wanted: long-lived versioned assets, fresh documents.
The migration itself was not one click
The first half was uneventful:
- Keep the Astro sites in static-output mode.
- Connect each Git repository to Cloudflare Pages.
- Set the build command and output directory.
- Deploy to the generated
pages.devURL. - Crawl the preview and compare it with production before touching DNS.
The work started when we connected the real domains.
Both projects use the apex domain as canonical: dogdoghk.com and boxmining.com. We also need www to work, but only as a permanent redirect. That sounds like a small requirement. On Cloudflare it crosses three different layers: the Pages custom domain, DNS, and an account or zone redirect rule.
Cloudflare’s Pages _redirects file handles paths, including thousands of old Boxmining URLs. It does not handle a hostname-level www.example.com to example.com redirect. Cloudflare’s own Pages guide for redirecting www to the apex uses a Bulk Redirect plus a proxied DNS record for www. A Single Redirect rule can also express the hostname change, but the DNS record still needs to send traffic through Cloudflare for the rule to run.
This was the fiddly part. A redirect can be logically correct and still fail because the hostname is not proxied, the custom domain is attached in the wrong place, or another rule runs first. We had to test the four entry points separately:
http://example.com
https://example.com
http://www.example.com
https://www.example.com
The finished behavior is boring, which is the goal. Every route reaches HTTPS on the apex domain, old path redirects preserve their destinations, and there is no redirect loop.
Cache tuning made the performance real
Moving to a large edge network does not fix weak cache policy by itself.
Our static builds produce fingerprinted assets such as app.A1B2C3.css or hashed fonts and images. When their contents change, the filename changes. Those files can safely receive a long browser and edge lifetime with immutable because a new deployment points to a new URL.
HTML is different. An article URL stays the same when the story is corrected. Caching it for a year would make publishing fast and updating miserable. We keep documents revalidatable, while giving selected machine-readable files their own policy. Boxmining, for example, can cache versioned assets aggressively while treating RSS, robots, sitemaps and the 404 page according to how often each should change.
Cloudflare gives you several overlapping ways to do this:
- a Pages
_headersfile deployed with the site; - dashboard Cache Rules that can alter eligibility, edge TTL, browser TTL and cache keys;
- purge controls when a bad response has already reached the edge;
- response headers such as
CF-Cache-StatusandAgefor checking what actually happened.
The flexibility is useful, but the dashboard does not make the precedence obvious. We changed a rule, deployed, inspected headers, found a mismatch, and repeated the loop. This is where Cloudflare feels more technical than Vercel: it gives you the controls, but it expects you to understand the request path.
The Cloudflare UI is the real downside
There is no polite way to say this. The Cloudflare dashboard is bad.
The navigation reflects the size of Cloudflare’s product catalogue, not the job of publishing one static website. “Workers & Pages” is already an awkward combined destination. Domain settings live elsewhere. Redirects may be under Rules or at account level. Cache behavior can come from the build output, a zone rule or a product default. Useful analytics exist, but they are split across screens with names that assume you already know Cloudflare’s mental model.
Vercel is much better at the happy path. Repository, deployment, domain and logs feel like parts of one product. Cloudflare feels like a network control panel that happens to include a static host.
The other side of that complaint is why we stayed. Cloudflare shows me more. It does not constantly turn the next useful control into a product-upgrade conversation. On the Free plan I can inspect DNS, see cache behavior, configure redirects, review security activity and understand which edge handled a request. I would rather fight a technical interface once than wonder what ordinary traffic will cost later.
When I would still choose Vercel
This is not a universal migration recommendation. I would still consider Vercel when:
- a team depends heavily on Next.js features and wants the closest platform integration;
- the deployment experience matters more than detailed network control;
- the project fits comfortably inside known usage and the business accepts metered growth;
- spend limits, alerts and a possible automatic pause are an acceptable safety model;
- developers need the simplest preview and collaboration workflow.
I would choose Cloudflare Pages when the output is genuinely static, traffic is public and unpredictable, and the project owner cares more about cost certainty and edge control than dashboard elegance.
For DogDogHK and Boxmining, that answer was clear. We accepted the worse interface, fixed the redirects, tuned the cache and ended up with hosting that fits the architecture instead of charging us as if every successful page view were a new product feature.
Our migration checklist
If you are making the same move, do not switch the nameservers first and work it out live. Use this order:
- Confirm the production build is static and identify every Function or middleware route.
- Check Pages limits against the built file count and largest asset.
- Deploy a preview and crawl pages, images, canonical tags, sitemaps and legacy redirects.
- Decide whether the apex or
wwwhostname is canonical. - Attach the custom domain, create proxied DNS records and add the hostname redirect at the edge.
- Set long immutable caching only for fingerprinted assets.
- Keep HTML revalidatable and give feeds, sitemaps and error pages deliberate policies.
- Test all HTTP, HTTPS, apex and
wwwcombinations withcurl -I. - Check
Cache-Control,CF-Cache-Status,Age,LocationandCF-Rayon live responses. - Keep the previous deployment available until DNS, redirects, analytics and search crawling are stable.
The hard part is not uploading the files. It is making the boring edges behave exactly as they did before, then using Cloudflare’s deeper controls without accidentally caching the wrong thing.
Sources and disclosure
DogDogHK and Boxmining are our own projects. The migration experience, speed preference and criticism of both dashboards are first-hand opinions. Live screenshots and response headers were checked on August 10, 2026. Platform limits, pricing and network figures were checked against the vendors’ current documentation on the same date.
- Cloudflare Pages pricing and the static-versus-Functions boundary
- Cloudflare Pages limits
- Cloudflare’s global network
- Cloudflare’s
wwwto apex redirect guide - Cloudflare Cache Rules
- Vercel pricing
- Vercel global network and regions
- Vercel usage calculation for static content
- Vercel Spend Management
- Vercel DDoS mitigation