Deploying This Blog: From Push to Live
Static files need a place to live, accessible from anywhere in the world.
Platform: Cloudflare Pages
Cloudflare Pages over other platforms, three reasons:
- Global CDN: edge nodes worldwide, visitors load from nearest location
- Zero-config HTTPS: automatic SSL certificate issuance and renewal
- Free tier: enough bandwidth and build minutes for a personal blog
Setup is simple: create a project in Cloudflare Pages dashboard, link your GitHub repo. Build commands and output directory are controlled in GitHub Actions, no need to configure in the dashboard.
One gotcha: Cloudflare Pages assigns a default domain <project>.pages.dev. The project name can’t be changed after creation. If you want to rename it, you have to delete and rebuild the project. I picked a random name at first and got stuck with it.
CI/CD Flow
Full deployment managed by GitHub Actions:
name: Deploy to Cloudflare Pages
on:
push:
branches: [master]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: peaceiris/actions-hugo@v3
with:
hugo-version: "0.164.0"
extended: true
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- name: Install dependencies
run: npm ci
- name: Patch theme for Hugo compatibility
run: bash scripts/patch-theme.sh
- name: Build
run: npm run build
- name: Deploy to Cloudflare Pages
run: npx wrangler pages deploy public --project-name=time-friend
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
Triggered by push to master or manual dispatch.
The theme patch step handles compatibility between the old theme and newer Hugo:
#!/bin/bash
find themes/hugo-brewm/layouts -type f -exec perl -i -pe'
s/site\.Language\.Locale/site.LanguageCode/g;
s/\.Language\.Locale/\.Language\.LanguageCode/g;
s/site\.Language\.Label/site.Language.LanguageName/g;
s/\.Language\.Label/\.Language\.LanguageName/g;
' {} +
Replaces deprecated .Locale and .Label with .LanguageCode and .LanguageName. Without this, the old theme throws errors on new Hugo.
Environment Variables
Cloudflare API Token and Account ID stored in GitHub Secrets:
CLOUDFLARE_API_TOKEN: API token with Pages deploy permission
CLOUDFLARE_ACCOUNT_ID: Cloudflare account ID
Configured in GitHub repo Settings > Secrets and variables > Actions. Auto-injected during CI runtime, never appears in logs.
First time setting this up, run a manual workflow to verify. Don’t ask how I know — spent an afternoon debugging a wrong token permission.
Domain
Domain time-friend.com DNS is managed by Cloudflare:
Arecord pointing to Cloudflare Pages load balancing IPCNAMEforwwwpointing totime-friend.pages.dev
Cloudflare Pages handles domain mapping automatically. HTTPS certificate auto-issued and renewed.
Redirects
static/_redirects handles URL redirection:
/archive/* /zh/archive/:splat 301
Old URL structure was /archive/xxx. After switching to bilingual, Chinese articles moved to /zh/archive/xxx. The redirect ensures old external links and search engine indexes still work.
Performance
Cloudflare’s global CDN is the key to performance. Static files cached at edge nodes, users fetch from nearest location, latency significantly reduced.
Caching is controlled client-side via Workbox. CDN-level caching doesn’t need extra config, Cloudflare Pages sets appropriate cache headers automatically. HTML files have short cache duration for content updates. Static assets (JS, CSS, images) have longer cache times.
Deploy to live takes about two minutes. Build is ~10 seconds, the rest is dependency installation and wrangler deployment.