Complete Guide to Migrating from GitHub Pages to Cloudflare Pages
- Published on
- ...
- Authors

- Name
- Huashan
- @herohuashan
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
| Feature | GitHub Pages | Cloudflare Pages |
|---|---|---|
| CDN Distribution | Depends on GitHub nodes | 300+ 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 Method | GitHub Actions | Git integration auto-deploy |
| Free Quota | Unlimited | 500 builds/month |
| HTTPS | ✅ Automatic | ✅ Automatic + Flexible SSL |
| Build Environment | Ubuntu Runner | Cloudflare Workers environment |
Migration Steps
Step 1: Prepare Cloudflare Pages Project
Login to Cloudflare Console Visit dash.cloudflare.com, enter Pages service
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)
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.0Note: Must set
HUGO_VERSIONenvironment variable, otherwise Cloudflare will use an older Hugo version that may cause build failure.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:
Add Custom Domain in Cloudflare Pages
- Go to project's Custom domains page
- Click Set up a custom domain
- Enter
geekhuashan.comandwww.geekhuashan.com
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.devWait 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:
Delete GitHub Actions Workflow
rm .github/workflows/hugo.yml git commit -m "Remove GitHub Actions workflow, fully migrated to Cloudflare Pages"Delete CNAME File (if exists) GitHub Pages uses
CNAMEfile in root orstatic/directory, not needed for Cloudflare Pages:rm CNAME # or rm static/CNAME git commit -m "Delete CNAME"Disable GitHub Pages
- Go to repository Settings → Pages
- 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
- In Cloudflare console left menu, enter Zero Trust
- First use requires setting a team name (e.g.,
geekhuashan-team) - Enter Access → Applications
4.2 Create Access Policy
Suppose you want to protect all life content under /posts/life/ path:
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/*
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.comOption C: One-time PIN
Policy name: One-time Password Access Action: Allow Rule: Include: Login Methods: One-time PIN Emails: - verified-[email protected]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 Node | GitHub Pages | Cloudflare Pages | Improvement |
|---|---|---|---|
| Tokyo | 1.2s | 0.3s | 75% |
| Singapore | 1.5s | 0.4s | 73% |
| Sydney | 2.1s | 0.5s | 76% |
| London | 0.8s | 0.3s | 62% |
| New York | 0.5s | 0.2s | 60% |
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
- Cloudflare Pages Official Documentation
- Cloudflare Access Getting Started Guide
- Hugo Deployment to Cloudflare Pages
- [tailscale-derp-guide](/My Tailscale DERP Self-hosted Guide)
Update Log:
- 2025-10-30: Completed migration from GitHub Pages to Cloudflare Pages
- 2025-10-30: Configured Cloudflare Access for private content access control
Related Posts
Bidirectional Links System Demo - Backlinks
Demonstrating the bidirectional links feature implemented in Hugo blog, inspired by knowledge management approaches in Obsidian and Roam Research.
Fixing NoMachine Menu Unresponsive Issue on macOS 26
Solving the bug in NoMachine 9.2.14 on macOS 26 (Tahoe) where secondary menus cannot be clicked, completely resolving menu unresponsiveness by modifying configuration files.