Logo

Complete Guide to Migrating from GitHub Pages to Cloudflare Pages

Published on
...
Authors

Why Migrate?

After hosting my personal blog on GitHub Pages for nearly a year, I decided to migrate to Cloudflare Pages. Two core requirements drove this decision:

1. Global CDN Acceleration

While GitHub Pages is reliable, access speed in some regions is not ideal. Cloudflare Pages comes with a global CDN that automatically distributes content across 300+ nodes worldwide, significantly improving global website access speed.

2. Fine-Grained Access Control

This is the most important reason for migration. I want my blog to support these scenarios:

  • Public Content: Technical articles, reading notes, etc., visible to everyone
  • Private Content: Life reflections, family photos/videos, etc., accessible only to authorized friends

GitHub Pages is a completely public static hosting service that cannot implement access control. Cloudflare Pages combined with Cloudflare Access can easily implement permission management based on email whitelists, Google OAuth, and other methods.

Technical Solution Comparison

FeatureGitHub PagesCloudflare Pages
CDN DistributionDepends on GitHub nodes300+ global CDN nodes
Access Control❌ None✅ Cloudflare Access
Custom Domain✅ Supported (requires CNAME)✅ Supported (DNS integration)
Build Speed~2-3 minutes~1-2 minutes
Deployment MethodGitHub ActionsGit integration auto-deploy
Free QuotaUnlimited500 builds/month
HTTPS✅ Automatic✅ Automatic + Flexible SSL
Build EnvironmentUbuntu RunnerCloudflare Workers environment

Migration Steps

Step 1: Prepare Cloudflare Pages Project

  1. Login to Cloudflare Console Visit dash.cloudflare.com, enter Pages service

  2. Create New Project

    • Click Create a project
    • Select Connect to Git
    • Authorize Cloudflare to access your GitHub repository
    • Select blog repository (e.g., geekhuashan/blog)
  3. Configure Build Settings

    Framework preset: Hugo
    Build command: hugo --gc --minify
    Build output directory: public
    Root directory: (leave empty)
    
    Environment variables:
    HUGO_VERSION: 0.135.0
    

    Note: Must set HUGO_VERSION environment variable, otherwise Cloudflare will use an older Hugo version that may cause build failure.

  4. First Deployment After saving, Cloudflare will automatically trigger the first build. You can view real-time logs on the Deployments page.

Step 2: Configure Custom Domain

Assuming your domain is geekhuashan.com and DNS is already hosted on Cloudflare:

  1. Add Custom Domain in Cloudflare Pages

    • Go to project's Custom domains page
    • Click Set up a custom domain
    • Enter geekhuashan.com and www.geekhuashan.com
  2. Automatically Configure DNS Records Cloudflare will automatically create CNAME records pointing to your Pages project:

    geekhuashan.com       CNAME   blog.pages.dev
    www.geekhuashan.com   CNAME   blog.pages.dev
    
  3. Wait for SSL Certificate Usually within 1-5 minutes, Cloudflare will automatically issue an SSL certificate.

Step 3: Remove GitHub Pages Configuration

After Cloudflare Pages deployment is complete and verified:

  1. Delete GitHub Actions Workflow

    rm .github/workflows/hugo.yml
    git commit -m "Remove GitHub Actions workflow, fully migrated to Cloudflare Pages"
    
  2. Delete CNAME File (if exists) GitHub Pages uses CNAME file in root or static/ directory, not needed for Cloudflare Pages:

    rm CNAME  # or rm static/CNAME
    git commit -m "Delete CNAME"
    
  3. Disable GitHub Pages

    • Go to repository SettingsPages
    • In Source dropdown select None
    • Save settings

Step 4: Implement Private Content Access Control

This is the core value of migration. Cloudflare Access allows setting access policies for specific paths.

4.1 Enable Cloudflare Access

  1. In Cloudflare console left menu, enter Zero Trust
  2. First use requires setting a team name (e.g., geekhuashan-team)
  3. Enter AccessApplications

4.2 Create Access Policy

Suppose you want to protect all life content under /posts/life/ path:

  1. Create Application

    • Click Add an application
    • Select Self-hosted
    • Configure basic information:
      Application name: Private Access for Life Reflections
      Session Duration: 24 hours
      Application domain: geekhuashan.com
      Path: /posts/life/*
      
  2. Configure Access Policy Cloudflare Access supports multiple authentication methods. Here are common solutions:

    Option A: Email Whitelist

    Policy name: Authorized Friends List
    Action: Allow
    Rule:
      Include:
        Emails:
          - [email protected]
          - [email protected]
          - [email protected]
    

    Option B: Google OAuth

    Policy name: Google Account Login
    Action: Allow
    Rule:
      Include:
        Login Methods: Google
        Emails ending in: @gmail.com
    

    Option C: One-time PIN

    Policy name: One-time Password Access
    Action: Allow
    Rule:
      Include:
        Login Methods: One-time PIN
        Emails:
          - verified-[email protected]
    
  3. Save and Test Access https://geekhuashan.com/posts/life/baby-1026, will redirect to Cloudflare Access login page.

Step 5: Optimize Hugo Configuration

Update baseURL in hugo.toml to match Cloudflare Pages domain:

baseURL = 'https://geekhuashan.com/'

Can also delete some GitHub Pages-only configurations:

- [params.github]
- repo = "geekhuashan/blog"

Deployment Flow After Migration

After migration, the blog update process becomes simpler:

hugo server -D

git add .
git commit -m "New article: XXX"
git push origin main

# No manual operation needed, takes effect in 1-2 minutes

Can view deployment status in Cloudflare Pages console in real-time:

  • 🟡 Building: Building
  • 🟢 Success: Deployment successful
  • 🔴 Failed: Build failed (check logs for troubleshooting)

Actual Results

Performance Improvement

Global access speed tested with WebPageTest:

Test NodeGitHub PagesCloudflare PagesImprovement
Tokyo1.2s0.3s75%
Singapore1.5s0.4s73%
Sydney2.1s0.5s76%
London0.8s0.3s62%
New York0.5s0.2s60%

Access Control Effect

Public content (like technical articles):

https://geekhuashan.com/posts/tech/tailscale-derp-guide
Direct access, no login required

Private content (like life reflections):

https://geekhuashan.com/posts/life/baby-1026
Redirects to Cloudflare Access login page
Enter authorized email to receive PIN code
Access content after verification
No repeated login needed within 24 hours

Troubleshooting

1. Hugo Version Mismatch

Problem: Deployment failed, logs show Error: module "xxx" not found

Cause: Cloudflare defaults to older Hugo version (0.54.0)

Solution: Add environment variable in project settings:

HUGO_VERSION = 0.135.0

2. baseURL Configuration Error

Problem: CSS/JS resources return 404, page styling lost

Cause: baseURL in hugo.toml still points to GitHub Pages domain

Solution: Update to Cloudflare Pages domain:

baseURL = 'https://geekhuashan.com/'

3. Cloudflare Access Infinite Redirect

Problem: Infinite redirect when accessing private path, cannot enter login page

Cause: Access Policy Path configuration doesn't match actual path

Solution: Ensure Path uses wildcard /posts/life/* instead of /posts/life

4. Build Cache Issue

Problem: After article update, Cloudflare deployment successful but content not updated

Cause: Cloudflare Pages cached old build artifacts

Solution: Clear cache in project settings, or add --gc in build command for cleanup:

hugo --gc --minify

Cost Analysis

Cloudflare Pages Free Quota

  • Monthly builds: 500 builds (plenty for personal blog)
  • Bandwidth: Unlimited
  • Requests: Unlimited
  • Custom domains: Unlimited
  • Concurrent builds: 1

Cloudflare Access Free Quota

  • Users: 50 free users
  • Applications: Unlimited
  • Authentication methods: Supports email PIN, Google, GitHub, etc.

For personal blogs, completely within free quota.

Summary

Migrating from GitHub Pages to Cloudflare Pages, main benefits:

Global CDN Acceleration: 70%+ access speed improvement in Asia-Pacific ✅ Flexible Access Control: Separate public/private content ✅ Faster Deployment: Build time reduced from 3 minutes to 1 minute ✅ Better Integration Experience: DNS, CDN, Access unified in Cloudflare management ✅ Zero Cost: Completely free for personal blogs

If you have similar needs - want to share technical content publicly while protecting private life content - Cloudflare Pages + Access is a perfect solution.

Further Reading


Update Log:

  • 2025-10-30: Completed migration from GitHub Pages to Cloudflare Pages
  • 2025-10-30: Configured Cloudflare Access for private content access control
Complete Guide to Migrating from GitHub Pages to Cloudflare Pages | 原子比特之间