إنتقل إلى المحتوى الرئيسي

Nine Years on GitHub Pages, Why I Finally Moved to Cloudflare

· 8 دقائق قراءة
Ammar Najjar
Engineering Team Lead & Software Architect
Migration from GitHub Pages to Cloudflare Pages

For nearly nine years, my personal website lived on GitHub Pages. It was free, reliable, and required almost no maintenance. So why migrate? I recently moved the entire site to Cloudflare Pages, and in this post I explain what motivated the change, the migration process, the issues I encountered, and the trade-offs I discovered along the way.

The site started as a Jekyll blog on June 18, 2016, and moved to Docusaurus in June 2025. GitHub Pages worked fine through all of it. The move to Cloudflare wasn't a reaction to something breaking; it was a deliberate step toward owning my online presence more seriously.

The real reason: I want to grow the site

GitHub Pages is a great fit for a small, self-contained portfolio. I was genuinely satisfied with it for nine years. It deployed on push, it was free, and I never had to think about it.

But my plans for the site have changed. I want to write more technical posts, publish more book summaries, and eventually offer some things to buy: courses, maybe photography prints or presets as a side hobby. When you start thinking about monetization, consistent traffic, and SEO, the picture changes. A github.io URL is not a brand. You can't build link equity around it the same way you can with a domain you own outright.

So the real question became: where do I register a custom domain, and what do I do with it once I have it?

Choosing a domain registrar

I looked at six registrars and compared them across price, ease of use, DNS quality, and extras.

ProviderPriceEase of UseDNSExtrasOverall
Cloudflare Registrar★★★★★★★★★☆★★★★★★★★★★10/10
Porkbun★★★★★★★★★★★★★★☆★★★★☆9.8/10
Namecheap★★★★☆★★★★★★★★★☆★★★★☆9.2/10
Spaceship★★★★★★★★★★★★★★☆★★★★☆9.1/10
Dynadot★★★★☆★★★★☆★★★★☆★★★★☆9.0/10
GoDaddy★★☆☆☆★★★★☆★★★☆☆★★☆☆☆6.5/10

Porkbun and Spaceship are genuinely good options. I went with Cloudflare Registrar because it charges at-cost prices (no markup, renewal equals first year), DNS is best-in-class, and it keeps everything in one place: my domain, my DNS, my hosting.

Cloudflare Pages vs GitHub Pages

Once the domain is registered through Cloudflare, pointing it at Cloudflare Pages is trivial. Attaching a custom domain to a Cloudflare Pages project takes a few minutes and zero DNS fiddling; the DNS records are managed in the same dashboard.

Here's how the two hosting platforms compare:

FeatureGitHub PagesCloudflare PagesWhy it matters
Global CDNGoodExcellentFaster page loads for visitors outside the US
HTTPSYesYesRequired for SEO and browser trust
Automatic buildsVia GitHub ActionsIncludedDeploy on every push without maintaining a workflow file
Preview deploymentsNoYesTest changes on a live URL before merging
AnalyticsNoneIncludedUnderstand where traffic comes from without a third-party script
Image optimizationNoYesServe correctly sized images automatically
Edge FunctionsNoYesRun server-side logic at the CDN edge without a separate backend
Build speedGoodFasterShorter feedback loop when deploying
Custom redirectsLimitedExcellentRedirect old URLs cleanly without code changes
Custom headersNoYes (via _headers file)Set CSP, cache-control, and other HTTP headers per route
Instant rollbacksNoYesRevert a bad deploy in one click from the dashboard
Private repositoryYes (free plan)Yes (free plan)Keep the source code private if needed

For my current needs, a static Docusaurus site, the most relevant differences are preview deployments (useful when testing i18n changes before merging) and the edge CDN (faster load times globally).

What it costs

For a personal blog, Cloudflare Pages, CDN, HTTPS, deployments, and preview builds are all free. The only real cost is the domain registration itself, which runs around €11/year, depending on the domain name, through Cloudflare Registrar.

What actually changed in the repository

This is the part that surprised me: the migration was almost nothing.

Removed: .github/workflows/deploy.yml, the GitHub Actions workflow that built the site and pushed to the gh-pages branch.

Added: wrangler.jsonc, 14 lines:

{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "website",
"compatibility_date": "2026-07-01",
"observability": {
"enabled": true,
},
"assets": {
"directory": "build",
},
"compatibility_flags": ["nodejs_compat"],
}

Updated: two scripts in package.json:

"deploy": "yarn run build && wrangler deploy",
"preview": "yarn run build && wrangler dev"

That's it. Deploying is now yarn deploy. Previewing locally with the Cloudflare runtime is yarn preview.

What went wrong

The migration itself was quick. The problems that followed were not obvious, and they are exactly the kind of thing you won't find in the official docs.

Package manager detection

Cloudflare's CI auto-detects the package manager from package.json. If your packageManager field isn't pinned, it may try to upgrade or switch package managers on its own. I had to pin it explicitly:

"packageManager": "yarn@1.22.22"

Without this, Cloudflare's build environment was attempting to auto-upgrade yarn, which broke the build.

Docusaurus url configuration

Docusaurus uses the url field in docusaurus.config.ts to generate absolute URLs for the sitemap, canonical tags, RSS feeds, and og:image. Mine was still set to the old GitHub Pages domain:

url: "https://ammarnajjar.github.io",

Everything built and deployed fine, but the sitemap was full of wrong URLs, og:image pointed nowhere, and RSS feed links were broken. The fix is straightforward: update url to your new custom domain before deploying.

url: "https://ammar-najjar.com",

This is easy to miss because the site looks correct in the browser; the broken parts are invisible unless you check the page source or run an SEO audit.

Custom domain setup in Cloudflare

Once the site is live at https://<project>.pages.dev, attaching a custom domain takes three steps in the Cloudflare dashboard:

  1. Go to Workers & Pages, open your project, then Custom domains
  2. Add your domain; Cloudflare automatically creates the DNS records since both the domain and the Pages project are in the same account
  3. Wait a few minutes for the TLS certificate to provision; the domain goes live automatically

If your domain is registered elsewhere, you would need to add a CNAME record manually. Registering through Cloudflare Registrar eliminates that step entirely.

Private GitHub repository

If you plan to make your repository private after connecting it to Cloudflare Pages, you need to ensure the Cloudflare GitHub App has access to private repositories. Go to your GitHub settings under Applications, find the Cloudflare Pages app, and verify it has access to the specific repo. Without this, builds will silently fail after you flip the repo to private.

The steps, in order

If you want to do the same thing:

  1. Create a Cloudflare account
  2. Connect your GitHub repository and deploy your site from Cloudflare Pages
  3. Verify it works at https://<project>.pages.dev
  4. Make any improvements you want; this is a good moment to audit the site
  5. Buy your custom domain through Cloudflare Registrar
  6. Attach the domain to the Pages project (a few minutes, no manual DNS work)
  7. Enable redirects so all traffic goes to your custom domain

The order matters: deploy first, then buy the domain. You want to confirm the site works before spending money, and you want the Pages project to already exist when you attach the domain to it.

The old ammarnajjar.github.io URL still works; it now redirects to ammar-najjar.com. Rather than relying on a server-side redirect (which GitHub Pages doesn't offer), the site's index.tsx does the job client-side: a meta http-equiv="refresh" for crawlers, a window.location.replace() for browsers, and a visible fallback link in case both fail. The full source is on GitHub.

One number

I don't have Lighthouse scores from the old GitHub Pages setup to compare against, so I won't invent a before/after benchmark. What I can share is that Cloudflare's analytics show a cache hit rate of around 80% for this site. That means 8 out of 10 requests are served directly from Cloudflare's edge without touching the origin at all. For a static blog with mostly returning visitors and repeat page views, that's the kind of number that translates directly to faster load times globally.

Was it worth it?

Yes. The deployment workflow is simpler, the domain and hosting are in one place, and I now have preview deployments, analytics, and instant rollbacks without any extra setup.

If you're happy with GitHub Pages and have no plans to grow the site, there's no reason to move. But if you're registering a custom domain anyway, doing it through Cloudflare and pointing it at Cloudflare Pages is the path of least resistance. The migration took an afternoon, and the result is a setup I expect to stay on for the next decade.

Nine years is a good run for any tool. GitHub Pages served its purpose. The switch wasn't a rejection; it was a change in ambition.

If you're hosting a personal site today, would you still choose GitHub Pages, or would you start with Cloudflare Pages? I'm curious what trade-offs others have found.