I've happily been using Netlify for several years, and was surprised to learn randomly during the covid scamdemic (not a typo) that cloudflare quietly put out a competing product. My initial tests of cloudflare pages back when it was new were disappointing with excrutiatingly slow build times, but boy has that ever changed!

Cloudflare has definitely made big strides in boosting performance of builds to their pages deployments, and it's now just as efficient and quick as (maybe even quicker than) Netlify. So the time to jump was nigh.

But, if I was happy with Netlify, why change?  Well the main reason was that cloudflare pages are served up with http/3 rather than just http/2. While that isn't a huge deal at the moment, I didn't feel like being stuck in the (recent) past as Netlify has shown no interest in upgrading.

Migration, ultimately, was very straightforward. I created a new pages project over at cloudflare pointed at my github repo

I then added a custom domain to this new pages setup:

This required me to update my DNS to change the existing CNAME record from Netlify to point to cloudflare.

I already had redirects setup on my Netlify deployment, by publishing them to a _redirects file, in the format:

/old/path1		/new/redirected/path1		308
/old/path2		/new/redirected/path1		302
/old/path3		/temp/redirection			301

Previous location, new location, redirect code. Pretty simple... and cloudflare pages support the exact same format in a file with the same name. Doesn't get any easier than that!

The only notable change I had to make was to add a new _headers file, because that previously was defined in a toml file for the netlify deployment.

My old netlify headers were defined thusly:

[[headers]]
  for = "/*"
  [headers.values]
    Strict-Transport-Security = "max-age=63072000; includeSubDomains; preload"

The new header is defined a little more succinctly in a _headers file as noted...

/*
  Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Basically just the applicable path to apply the header(s) to, and then the headers on indented lines following that. Pretty simple stuff.

I did have a function in place on netlify to push sitemap updates to google on deploy, but google has deprecated that endpoint so I didn't migrate the post-deploy function to cloudflare, but yep - it totally supports those too.

One of the other niceties of cloudflare pages is that it has built in analytics, which if all you care about is seeing core web vitals and basic visits, it could be a viable, lighter weight, replacement for google analytics:

For just basic visits, by page & country/etc, this is totally serviceable data and you may not need anything beyond this. It's really incredible that this is all free!

The last bit of config I did was to ensure that my site is only accessible at the custom domain, not the .pages.dev domain – other than preview deploys, which, oh yes are also included, and better can be locked down to only be accessible to people you specify should be allowed to access them!

I left the netlify deployment in place to ensure that I wouldn't break things for anyone in case the DNS hadn't propagated, but now that it's been humming along nicely on cloudflare for over a week, I should probably get around to removing that.

In all, this was easily the most painless migration I've ever done.

In case you're curious - the site in question is the site for my voiceover business, which is built with the static sitebuilder, Eleventy, Tailwind CSS and a sprinkle of AlpineJS. I'm very pleased with how easy it is to maintain and extend, and especially with how quickly it builds and even more so how fast it's served.

If you're in the market for any voice work, be it for a video game, phone messaging, training materials for an app, or anything else – I'd love to hear from you! Check out the site over here at willvincentvoice.com.

Update:

So there was one small issue I had to sort out when I shut off Netlify. Because cloudflare pages don't allow you to setup an A record, unless you let cloudflare manage the domain anyway, and Netlify did, I needed to setup a redirect from the non-www version of my domain to the www version that is setup as the custom domain for the cloudflare pages hosted site.

Conveniently, I'm already running a bunch of miscellaneous services with docker on my server, and I use traefik to shuttle traffic around between those various docker instances. So setting up a redirect was basically just a matter of configuring a simple container. While in theory I should be able to do it with just traefik, I couldn't get it to work and ended up using the morbz/docker-web-redirect docker container setting the env var VIRTUAL_HOST to my non-www domain, and the REDIRECT_TARGET to the www version. I also included relevant traefik config to rewrite http to https, etc.

So, one small wrinkle if you're not letting cloudflare manage your domain I guess.