📋 Background

My blog was previously hosted on a Raspberry Pi 5 with Cloudflare Tunnel for HTTPS. While this worked, it had several issues:

⚠️ The Problem: Cloudflare Tunnel requires a persistent connection to Cloudflare edge nodes, which are frequently blocked or throttled by GFW in China. This caused frequent disconnections and 530 errors.

✅ Solution: Cloudflare Pages

Cloudflare Pages hosts static sites directly on Cloudflare's CDN, eliminating the need for a tunnel. Combined with Git auto-deploy, it provides:

🔧 Migration Steps

1. Prepare Git Repository

First, I needed to push my website to GitHub. But there was a problem:

⚠️ SSH Connection Issue: SSH to GitHub was timing out because GFW blocks direct SSH connections.

Initial attempt (failed):

# SSH connection kept timing out
ssh -T git@github.com
# Connection timed out

Solution: Use HTTPS instead of SSH

# Configure Git to use HTTPS
git config --global url."https://github.com/".insteadOf git@github.com:

# Generate GitHub Personal Access Token
# Go to: https://github.com/settings/tokens
# Create fine-grained token with: Repository permissions → Contents → Read and write

# Cache credentials (one-time setup)
git config --global credential.helper store
echo "https://USERNAME:TOKEN@github.com" > ~/.git-credentials
chmod 600 ~/.git-credentials
🔐 Security Note: Never share your GitHub token! I'm using a fine-grained token with 30-day expiry and only repo scope for this specific repository.

Test connection:

cd /home/henry/website
git remote add origin https://github.com/henryjin8s/henryjin8s-blog.git
git push -u origin main

2. Create Cloudflare Pages Project

⚠️ 2026 Update: Cloudflare has merged Pages into Workers platform. In the new UI, you'll see the Workers icon (⚡ lightning) for both. The key difference is selecting "Pages" vs "Worker" during creation.

Steps:

  1. Go to Cloudflare Dashboard
  2. Workers & Pages → Create Application
  3. Select Pages (diamond icon ◇)
  4. Connect to Git → Select henryjin8s/henryjin8s-blog
  5. Configure:
    • Production branch: main
    • Build command: (leave empty - static site)
    • Deploy command: true (required field)
    • Root directory: /
  6. Save and Deploy

3. Configure Custom Domain

After the initial deployment:

  1. Go to Pages project → Custom domains
  2. Add henryjin8s.xyz
  3. Cloudflare will show DNS configuration:
Type:   CNAME
Name:   @
Target: henryjin8s-blog.pages.dev
Proxy:  Enabled (orange cloud) ☁️

⚠️ DNS Propagation: Changes may take 5-30 minutes to propagate.

4. Test Git Auto-Deploy

Now the fun part - test if pushing to Git triggers automatic deployment:

# Make a change
cd /home/henry/website
git add .
git commit -m "Update homepage with new posts"
git push

# Watch Cloudflare Dashboard → Deployments
# Should see: "Production deployment" → "Success" in ~1-2 minutes
✅ Success! After pushing, Cloudflare automatically:
  1. Detects the new commit via webhook
  2. Pulls the latest code from GitHub
  3. Deploys to global CDN
  4. Invalidates cache

🔍 Troubleshooting

Problem 1: Confused by UI Changes (2026)

Symptom: Not sure if creating Pages or Worker - both show lightning icon (⚡).

Solution: Cloudflare merged Pages into Workers platform in 2025. Look for:

Problem 2: Git Push Permission Denied

Symptom: fatal: unable to access: The requested URL returned error: 403

Solution: GitHub token doesn't have write permissions. Update token scopes:

# Fine-grained token needs:
# Repository permissions → Contents → Read and write

Problem 3: Old Content Still Showing

Symptom: Website shows old content after deployment.

Solution: Cloudflare cache might be stale. Purge cache:

# Cloudflare Dashboard → Caching → Configuration → Purge Everything
# Or wait 5-10 minutes for automatic refresh

📊 Before vs After

Aspect Before (Tunnel) After (Pages)
Deployment Manual script Git push (auto)
Uptime ~90% (tunnel drops) ~99.9% (CDN)
Performance Single server (Pi5) Global CDN
Resource Usage Pi5 CPU + bandwidth Zero (offloaded)
GFW Resilience ❌ Frequent blocks ✅ Much better

🔐 Security Considerations

GitHub Token Security

What I Did

# Token stored locally (not in Git)
~/.git-credentials (chmod 600)

# Token configuration:
- Type: Fine-grained
- Expiration: 30 days
- Repository: henryjin8s-blog only
- Permissions: Contents (Read and write)

🔄 Current Workflow

Now my blog update workflow is simple:

# 1. Edit content locally
cd /home/henry/website
vim posts/new-post.html

# 2. Commit and push
git add .
git commit -m "Add new blog post"
git push

# 3. Wait ~1-2 minutes
# Cloudflare automatically deploys

# 4. Visit https://henryjin8s.xyz
# New content is live!

💡 Lessons Learned

  1. UI Update (2026): Cloudflare merged Pages into Workers platform. Both use lightning icon (⚡) now. Focus on selecting "Pages" workflow during creation, not the icon.
  2. HTTPS > SSH in China: SSH connections to GitHub are frequently blocked. HTTPS with token works reliably.
  3. Fine-grained tokens: More secure than classic tokens. Can limit to specific repos and set expiration.
  4. Cloudflare CDN > Tunnel: For static sites in China, direct CDN hosting is more reliable than Tunnel.
  5. Test before migrating domain: Test on .pages.dev subdomain first, then switch custom domain.

✅ Conclusion

The migration from Cloudflare Tunnel to Cloudflare Pages was a huge improvement. The site is now more reliable, faster, and requires zero maintenance. Git auto-deploy means I can focus on writing content instead of managing infrastructure.

Total migration time: ~2 hours (including debugging the Worker vs Pages confusion)

Cost: Free (Cloudflare Pages free tier)

Next steps: Maybe add a CMS for non-technical content updates? 🤔