Why migrate to Cloudflare Pages?

The original blog ran on a Tencent Cloud server with Flask and Nginx. It worked, but required server maintenance, renewals, HTTPS configuration, and deployment management. After the migration, static pages are delivered through Cloudflare's global CDN, APIs run on Pages Functions, and article data is stored in KV.

Main benefits:

  • No server or Nginx maintenance
  • Automatic HTTPS and global CDN delivery
  • Frontend deployments from Git or Wrangler
  • Article updates are written directly to KV without rebuilding the site

The new architecture

Frontend: Astro static site
API: Cloudflare Pages Functions
Storage: Cloudflare KV
Admin: Static HTML management panel
Domain: Cloudflare DNS

The request flow is simple:

Browser -> Astro page -> /api/posts -> Pages Functions -> KV

Step 1: Prepare the Astro project

Create the project and install dependencies:

npm create astro@latest my-blog
cd my-blog
npm install

Put public assets in public/ and pages in src/pages/. Running npm run build generates the static site in dist/.

Step 2: Create Pages Functions

Create functions/api/[[path]].js in the project root. This catch-all route can handle:

  • GET /api/posts: list posts
  • GET /api/posts/:id: get a full post
  • POST /api/posts: create a post
  • PUT /api/posts/:id: update a post
  • DELETE /api/posts/:id: delete a post
  • POST /api/auth/login: admin login

Functions access KV through context.env.BLOG_KV.

Step 3: Configure KV

Create a KV namespace:

npx wrangler kv namespace create BLOG_KV

Bind it in wrangler.toml:

name = "my-blog"
pages_build_output_dir = "./dist"

[[kv_namespaces]]
binding = "BLOG_KV"
id = "your-namespace-id"

Store all posts under a posts key, divided into papers and tips collections.

Step 4: Build the admin panel

Place the admin page at public/admin/index.html. Astro copies it to dist/admin/ during the build. The page uses the API to authenticate and manage posts.

A bilingual post can use these fields:

{
  "id": "article-id",
  "title": "Chinese title",
  "titleEn": "English title",
  "summary": "Chinese summary",
  "summaryEn": "English summary",
  "content": "Chinese Markdown",
  "contentEn": "English Markdown",
  "tags": ["Cloudflare", "Astro"]
}

Step 5: Deploy

Build the site first:

npm run build

Deploy it to Cloudflare Pages:

npx wrangler pages deploy ./dist --project-name my-blog

Cloudflare returns a pages.dev URL. Verify the pages, Functions, and KV before adding a custom domain.

Step 6: Add a custom domain

Add the domain in the Cloudflare Pages project settings. If the domain already uses Cloudflare DNS, the required record is usually created automatically. Cloudflare also provisions the HTTPS certificate.

Daily workflow

For frontend changes:

Edit code -> npm run build -> Git push or Wrangler deploy

For publishing content:

Open /admin/ -> edit Markdown -> save -> KV updates immediately

Important notes

  • Never commit an API token to Git
  • Add .wrangler/, .astro/, dist/, and .env to .gitignore
  • Do not use default plaintext credentials for the admin panel
  • Export and back up important KV content regularly

The result is a serverless blog with dynamic content management and much less operational work.