And just like that, I was done with Netlify

As someone who uses Netlify’s Starter plan to host this site, the following situation is the kind of nonsense that keeps me up late at night…

On February 24th, an invoice was issued to a Starter plan user of Netlify for a substantial amount of bandwidth usage.

Our internal systems should have identified this rare case, and it should have been flagged by our systems before being sent to this user. We sincerely apologize for this situation and the anxiety it caused. Please know that we are taking this situation very seriously.

This situation has reinforced the urgency of our ongoing work to put safeguards in place in our billing system to ensure that this never happens again. No user of Netlify should ever be concerned that this could happen to them, and we are committed to fixing this issue immediately.

We will also be looking at our company practices, as there clearly was an opportunity for us to have better handled this situation.

Again, we apologize to the impacted user, as well as the community at large. We promise to do better.

Netlify (@Netlify) February 27, 2024

The fact that a small static site hosted for free could potentially cost thousands of dollars is insane. Up until now, I assumed hitting any bandwidth limits would take the site down until the next billing period (or pay any overage fees to turn it back on).

To mitigate the risk of this happening to me, I shut down Netlify deployments and migrated to Cloudflare Pages. On paper Cloudflare’s features are way more attractive and don’t imply free plans will be charged for going over any limits.

Free plan comparison

BandwidthUnlimited100GB per month (then $55 per 100GB)
Builds1 concurrent build, 500 times per month1 concurrent build, 300 minutes per month (then $7 per 500)
Preview deployments
Custom domains100 per project~90 apex domains
SSL certificate
Site analyticsFree$9 per site per month
FilesSites can contain 20,000 files — maximum asset size is 25 MiBLimit of 54,000 files per directory — maximum asset size is 100 MiB
Header rulesMaximum of 100 rules-
RedirectsMaximum of 2,000 static redirects and 100 dynamic redirects. Bulk Redirects allow for much more at the account level.No apparent limit, recommended to keep under 10k redirects
Content scraping protectionProtects text, images and email addresses from web scrapers-
MembersPages site can be managed by an unlimited number of users via the Cloudflare dashboardLimited to 1 free member, with unlimited Git Contributors

Cloudflare Pages

Migrating from Netlify to Cloudflare was painless:

  1. Connect GitHub repository to Cloudflare Pages.
  2. Configure Hugo build command.
  3. Validate * site builds when pushing commits to GitHub.
  4. Copy DNS records from Netlify over to Cloudflare.

The hardest part was waiting for all the records to propagate across the internet.

My custom _headers and _redirects files used by Netlify ported over great too as Cloudflare Pages uses the same. Cloudflare Pages build log output

One snag I encountered during the migration was long build times. In my previous workflow, I set up GitHub Actions and Netlify to cache all generated static assets between builds. For a site like mine with thousands of images, this slashes a cold build from 18 minutes down to a minute for incremental builds.

Build cache settings in Cloudflare dashboard.

Cloudflare Pages does support build caching, but only for Gatsby, Next.js, and Astro frameworks. It’s a beta feature, so I expect Hugo to be added in the future. For now I’ll have to live with 18-minute builds — unless I decide to use a GitHub Action again to build then deploy to Cloudflare using their Wrangler CLI. We’ll see…

Cloudflare Web Analytics dashboard showing Core Web Vital scores for

Another bonus, Cloudflare gives you privacy-first site analytics for free too. Which could be an option if I decide Google Analytics at some point.

And hell, if Cloudflare Pages turns out to be a dud or too restrictive I can always go back to self-hosting on the cheap.