· 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ị output | Cần adapter | Khi nào dùng |
|---|---|---|---|
| Hoàn toàn tĩnh | 'static' (mặc định) | Không | Trang nội dung, không có logic server |
| All-SSR | 'server' | Có | Route động, auth, truy cập DB |
| Kết hợp | 'hybrid' | Có | 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
- Vào dash.cloudflare.com → Workers & Pages → Create application → Pages → Connect to Git
- Cấp quyền cho GitHub và chọn repository của bạn
- Cấu hình build settings:
| Cài đặt | Repo đơn app | pnpm monorepo |
|---|---|---|
| Root directory | (để trống) | apps/site |
| Build command | pnpm run build | pnpm run build |
| Build output directory | dist | dist |
| Phiên bản Node.js | 22 | 22 |
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 đó.
- 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 → Settings → Environment 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 quaimport.meta.env.PUBLIC_*. Mọi thứ còn lại chỉ có ở phía server. process.envkhông khả dụng trong Workers runtime. Thay vào đó dùngcloudflare: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 domains → Set 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: Settings → Environment 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ỗi | Nguyên nhân | Cách xử lý |
|---|---|---|
No adapter installed | output: 'server' mà không có adapter | Chạy npx astro add cloudflare@12 |
Cannot find module 'node:crypto' | Thiếu flag nodejs_compat | Thêm vào compatibility_flags trong wrangler.jsonc |
env.MY_VAR trả về undefined | Dùng process.env trong Workers runtime | Chuyển sang import { env } from 'cloudflare:workers' |
Secret undefined lúc build | Secrets chỉ khả dụng tại runtime | Chuyển đọc secret sang trang server-rendered, không phải prerendered |
| 404 sau khi deploy | Chư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 đột | Thêm "disable_nodejs_process_v2" vào compatibility_flags |
Giới hạn gói free (tóm tắt)
| Tài nguyên | Giới hạn free |
|---|---|
| Băng thông tĩnh | Không giới hạn |
| Build | 500/tháng, 1 concurrent, timeout 20 phút |
| Lượt gọi Functions | 100.000/ngày |
| Custom domain | 100 mỗi project |
| D1 đọc | 5M/ngày |
| D1 ghi | 100K/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.