· astro / cloudflare / cloudflare-pages

Cách deploy trang Astro lên Cloudflare Pages từng bước

Cloudflare Pages deploy Astro miễn phí, không giới hạn băng thông. Từng bước: cài adapter, fix cạm bẫy root directory monorepo, biến môi trường, custom domain.

Bởi Ethan

1.693 từ · 9 phút đọc

Cloudflare Pages là cách nhanh nhất để đưa một dự án Astro lên mạng với phạm vi phủ sóng toàn cầu. Build tĩnh miễn phí, không giới hạn băng thông. SSR chạy trên Workers runtime ở edge, với 100.000 lượt gọi hàm mỗi ngày trên gói free. Toàn bộ quá trình setup mất chưa đến hai mươi phút — nếu bạn biết trước một cạm bẫy mà hầu như mọi người dùng monorepo đều vấp phải (sẽ trình bày ở bước 3).

Hướng dẫn này dùng @astrojs/cloudflare v12 (phiên bản cuối cùng hỗ trợ chính thức Cloudflare Pages — v13, ra mắt tháng 3/2026, đã bỏ Pages để chuyển sang Cloudflare Workers), Astro 6.3.1, và Wrangler ^4.90.0.

Dành cho ai

Developer đã có dự án Astro trên máy, biết cơ bản về Git và CLI, và có tài khoản Cloudflare. Nếu bạn chưa chọn platform deploy, đọc Các platform deploy tốt nhất cho trang tĩnh trước. Nếu bạn đang cân nhắc giữa Cloudflare và AWS, xem Cloudflare Workers vs AWS Lambda.

Yêu cầu trước khi bắt đầu

  • Node.js ≥18 — Workers runtime yêu cầu điều này; khuyến nghị ≥22 (node -v để kiểm tra)
  • pnpm, npm, hoặc yarn — pnpm cho monorepo
  • Tài khoản Cloudflare — gói free là đủ
  • Dự án Astro đã commit lên GitHub
  • Adapter @astrojs/cloudflare — chỉ cần nếu bạn dùng SSR hoặc hybrid rendering; trang thuần tĩnh bỏ qua phần này

Nếu bạn đang dùng output: 'static' (mặc định của Astro), nhảy thẳng đến Bước 3. Adapter chỉ cần thiết cho các route render phía server.

Bước 1: Chọn chế độ render

Cloudflare Pages hỗ trợ cả ba output mode của Astro. Chọn sai là sẽ gặp lỗi âm thầm, không có thông báo rõ ràng.

Chế độGiá trị outputCần adapterKhi nào dùng
Hoàn toàn tĩnh'static' (mặc định)KhôngTrang nội dung, không có logic server
All-SSR'server'Route động, auth, truy cập DB
Kết hợp'hybrid'Chủ yếu tĩnh, một số route on-demand

Điều gì xảy ra nếu chọn sai:

  • output: 'server' mà không có adapter → build thất bại: No adapter installed.
  • output: 'static' với API endpoints → endpoints bị loại bỏ âm thầm tại build time.

Bước 2: Cài adapter và cập nhật astro.config.mjs

Chạy trình cài tích hợp — nó sẽ tự vá config của bạn:

npx astro add cloudflare@12
# hoặc với pnpm:
pnpm dlx astro add cloudflare@12

Config của bạn sẽ trông như thế này:

import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  output: 'server',         // hoặc 'hybrid'
  adapter: cloudflare({
    platformProxy: { enabled: true },  // bật CF bindings trong local dev
  }),
});

Tùy chọn platformProxy giúp Cloudflare bindings (KV, D1, v.v.) hoạt động được trong quá trình astro dev. Thiếu nó, mọi lần truy cập binding đều sẽ throw lỗi khi dev.

Thêm wrangler.jsonc cho SSR

Nếu bạn dùng SSR, tạo file wrangler.jsonc ở thư mục gốc của project (hoặc thư mục app trong monorepo):

{
  "$schema": "https://json.schemastore.org/wrangler.json",
  "name": "my-astro-app",
  "compatibility_date": "2026-05-01",
  "compatibility_flags": ["nodejs_compat"],
  "assets": {
    "binding": "ASSETS",
    "directory": "./dist"
  },
  "observability": { "enabled": true }
}

Flag nodejs_compat là bắt buộc. Thiếu nó, bất kỳ Node.js built-in nào (node:crypto, node:path, v.v.) đều sẽ throw lỗi runtime mà không có thông báo hữu ích — request trả về 500.

Bước 3: Kết nối Pages với GitHub

  1. Vào dash.cloudflare.comWorkers & PagesCreate applicationPagesConnect to Git
  2. Cấp quyền cho GitHub và chọn repository của bạn
  3. Cấu hình build settings:
Cài đặtRepo đơn apppnpm monorepo
Root directory(để trống)apps/site
Build commandpnpm run buildpnpm run build
Build output directorydistdist
Phiên bản Node.js2222

Cạm bẫy với monorepo

Root directory bị ẩn dưới mục “Advanced settings” — hãy mở accordion đó trước khi nhấn Save. Nếu bỏ qua, Pages sẽ build từ thư mục gốc của repository. Build vẫn chạy, thoát với mã 0, deploy thành công, và phục vụ trang 404 hoặc trang trống vì đã deploy nhầm thư mục dist/.

Trong setup của toolchew (Astro 6.3.1 + pnpm + Turbo), Turbo build apps/site/dist/ đúng cách. Nhưng Pages vẫn cần Root directory = apps/site để phục vụ từ đúng thư mục. Đây là lỗi deploy đầu tiên phổ biến nhất của người dùng monorepo, và nó không tạo ra thông báo lỗi nào — chỉ là một deployment trống rỗng.

Docs monorepo của Cloudflare có đề cập tùy chọn này nhưng không có ví dụ nào kết hợp pnpm + Turbo + Astro. Cách xử lý thực tế nằm trong các thread cộng đồng và cái accordion bị thu gọn đó.

  1. Nhấn Save and Deploy. Build đầu tiên mất 45–90 giây (cài package ~30s, Astro build ~20–40s).

Trang của bạn đã live tại <project>.pages.dev. Mỗi lần push lên main sẽ kích hoạt một production build mới.

Bước 4: Biến môi trường

Local development: .dev.vars

Wrangler đọc file .dev.vars (được gitignore) trong quá trình wrangler dev hoặc astro dev:

DB_PASSWORD=myPassword
API_SECRET=dev-secret-key

Production: Pages dashboard

Vào Pages project → SettingsEnvironment variables. Đặt biến theo từng môi trường — Production hoặc Preview. Đánh dấu secrets là Encrypt (chỉ ghi sau khi lưu; Cloudflare không thể hiển thị lại chúng).

Truy cập biến khi runtime

Adapter v12 (hướng dẫn này):

const { env } = Astro.locals.runtime;
const dbUrl = env.DATABASE_URL;

Hoặc type-safe thông qua astro:env (ổn định từ Astro 5.0.0; @astrojs/cloudflare v12.2.0 đã hoàn thiện hỗ trợ secrets cho API này):

import { DATABASE_URL } from 'astro:env/server';

Ba quy tắc để tránh bất ngờ

  • Biến PUBLIC_* được expose ra browser tại build time thông qua import.meta.env.PUBLIC_*. Mọi thứ còn lại chỉ có ở phía server.
  • process.env không khả dụng trong Workers runtime. Thay vào đó dùng cloudflare:workers.
  • Secrets đặt trong dashboard chỉ khả dụng tại runtime — không phải trong quá trình prerendering. Nếu một trang prerender đọc secret, nó sẽ nhận về undefined.

Bước 5: Custom domain

Vào Pages project → Custom domainsSet up a custom domain.

Hai hướng tùy vào DNS của bạn đang ở đâu:

Cloudflare DNS (khuyến nghị): chỉ cần một click để cấu hình CNAME, DNS và SSL phân giải trong khoảng hai phút.

DNS bên ngoài: thêm CNAME trỏ vào <project>.pages.dev. SSL được cấp qua Let’s Encrypt — chờ tối đa 24 tiếng. ERR_SSL_PROTOCOL_ERROR trong khoảng thời gian này là bình thường; đó không phải cấu hình sai.

Bước 6: Preview deployment

Mỗi lần push branch đều tạo ra một preview URL tại https://<branch>.<project>.pages.dev. Tính năng này hoạt động ngay mà không cần cấu hình gì thêm.

Để giới hạn biến môi trường cho preview: SettingsEnvironment variables → bật tab Preview. Secrets đặt dưới Production không lọt vào Preview build.

Preview build miễn phí và không tính vào 500 build/tháng của bạn.

Bước 7: Đi xa hơn — D1, KV, và Workers

Thêm database D1

Trong wrangler.jsonc:

{
  "d1_databases": [{
    "binding": "DB",
    "database_name": "my-db",
    "database_id": "<uuid>"
  }]
}

Rồi trong một trang server-rendered hoặc API route:

import { env } from 'cloudflare:workers';
const posts = await env.DB.prepare('SELECT * FROM posts').all();

Gói D1 free: 5 triệu lượt đọc và 100.000 lượt ghi mỗi ngày. 5 GB lưu trữ. Đủ dùng cho hầu hết các dự án indie.

Khi nào nên chuyển sang Workers

Pages Functions đáp ứng phần lớn nhu cầu. Chuyển sang Workers deployment đầy đủ khi bạn cần:

  • Cron triggers — Pages không có cron tích hợp sẵn
  • Durable Objects — có trên cả Workers Free và Workers Paid; gói free bao gồm 100K request/ngày và 5 GB lưu trữ (chỉ SQLite backend)
  • Service bindings — gọi một Worker từ Worker khác
  • CPU time liên tục trên 10ms mỗi request (Pages Functions gói free: 10ms/invocation; Workers Paid: 30M CPU-ms/tháng)

Trang Astro vẫn giữ nguyên — chỉ có cơ chế deploy thay đổi từ Pages Git integration sang wrangler deploy trong CI. Xem Cloudflare Workers vs AWS Lambda để biết khi nào gói trả phí xứng đáng.

Lỗi thường gặp

LỗiNguyên nhânCách xử lý
No adapter installedoutput: 'server' mà không có adapterChạy npx astro add cloudflare@12
Cannot find module 'node:crypto'Thiếu flag nodejs_compatThêm vào compatibility_flags trong wrangler.jsonc
env.MY_VAR trả về undefinedDùng process.env trong Workers runtimeChuyển sang import { env } from 'cloudflare:workers'
Secret undefined lúc buildSecrets chỉ khả dụng tại runtimeChuyển đọc secret sang trang server-rendered, không phải prerendered
404 sau khi deployChưa đặt root directory cho monorepoĐặt Root directory thành apps/site trong Advanced settings
Trang SSR trả về [object Object]compatibility_date ≥ 2025-09-15 + nodejs_compat xung độtThêm "disable_nodejs_process_v2" vào compatibility_flags

Giới hạn gói free (tóm tắt)

Tài nguyênGiới hạn free
Băng thông tĩnhKhông giới hạn
Build500/tháng, 1 concurrent, timeout 20 phút
Lượt gọi Functions100.000/ngày
Custom domain100 mỗi project
D1 đọc5M/ngày
D1 ghi100K/ngày
KV đọc (production)100K đọc/ngày, 1K ghi/ngày, 1 GB lưu trữ — gói free

Liên quan: Các platform deploy tốt nhất cho trang tĩnh so sánh Pages với Vercel, Netlify, và Render về giá và developer experience. Nếu bạn đang chọn framework, Astro vs Hugo đề cập đến sự đánh đổi về tốc độ build và Next.js vs Astro giải thích khi nào nên dùng framework dựa trên React.