Astro là một lựa chọn rất hợp cho blog cá nhân: nội dung viết bằng Markdown, trang được render sẵn thành HTML tĩnh, ít JavaScript và dễ đưa lên CDN. Cloudflare Pages là phần còn lại của bức tranh: nối repo GitHub, chạy build mỗi khi có commit và phân phối file tĩnh qua mạng lưới của Cloudflare.
Bài này ghi lại quy trình mình đang dùng cho blog này, từ lúc tạo project Astro đến khi có tên miền riêng chạy HTTPS. Các bước không phụ thuộc vào một template cụ thể; bạn có thể áp dụng cho blog, portfolio hoặc site tài liệu.
Bài viết tập trung vào static site — cách đơn giản và rẻ nhất. Nếu bạn cần SSR, API Routes hoặc xử lý request động, xem phần cuối bài.
Bạn sẽ làm gì
Luồng tổng quát:
Máy local ──git push──> GitHub ──webhook──> Cloudflare Pages
│
├─ pnpm build
├─ tạo thư mục dist/
└─ deploy lên CDN
│
Tên miền riêng ──HTTPS──> Cloudflare CDN <─────────────┘
Có bốn phần chính:
- Tạo và chạy thử project Astro ở local.
- Cấu hình build và đưa code lên GitHub.
- Kết nối GitHub với Cloudflare Pages.
- Gắn tên miền, bật HTTPS và kiểm tra sau deploy.
1. Chuẩn bị
Bạn cần:
- Node.js bản LTS hoặc bản mà project yêu cầu. Blog này đang ghim Node
24.22.1. - pnpm — blog này dùng pnpm
12.15.0. Nếu chưa có, bật qua Corepack:corepack enable corepack prepare [email protected] --activate - Một tài khoản GitHub.
- Một tài khoản Cloudflare.
- Tên miền nếu muốn dùng domain riêng. Không có cũng không sao: Cloudflare sẽ cấp một địa chỉ
*.pages.dev.
Kiểm tra nhanh:
node -v
pnpm -v
git --version
2. Tạo project Astro
Tạo project mới:
pnpm create astro@latest my-blog
cd my-blog
pnpm install
pnpm dev
Khi được hỏi, chọn template Blog nếu muốn có sẵn layout, RSS và Markdown; chọn Minimal nếu muốn tự dựng từ đầu. Có thể truyền template trực tiếp:
pnpm create astro@latest my-blog --template blog
Mở http://localhost:4321 để xem thử. Sau đó build thử bản production:
pnpm build
pnpm preview
Nếu thành công, Astro tạo thư mục dist/. Đây chính là thứ Cloudflare Pages sẽ deploy. Kiểm tra nhanh:
ls dist
Bạn sẽ thấy index.html, các thư mục asset và có thể cả rss.xml, sitemap-index.xml tùy template.
3. Cấu hình Astro cho production
Mở astro.config.mjs (hoặc .ts) và đặt site thành domain thật:
import { defineConfig } from "astro/config";
export default defineConfig({
site: "https://example.com",
trailingSlash: "always",
});
Vì sao cần site?
- Astro và các integration như RSS, sitemap, canonical dùng nó để tạo URL tuyệt đối.
- Nếu để trống hoặc để
localhost, các URL này sẽ sai khi lên production. - Nếu sau này đổi domain, nhớ sửa
site, canonical và redirect.
trailingSlash: "always" là tùy chọn, nhưng nên chọn một kiểu và giữ nhất quán. Blog này dùng dấu / ở cuối URL, ví dụ /posts/ten-bai/. Nếu bạn dùng kiểu không có dấu /, đặt "never" và kiểm tra lại link nội bộ.
Ghim phiên bản Node và pnpm
Tạo file .nvmrc ở root:
24.22.1
Trong package.json, giữ trường packageManager:
{
"packageManager": "[email protected]",
"engines": {
"node": ">=24.22.1 <25"
}
}
Cloudflare Pages đọc .nvmrc (hoặc .node-version) để chọn Node. Với pnpm, build image v3 không tự suy ra version từ pnpm-lock.yaml, nên ta sẽ đặt PNPM_VERSION ở bước sau. Trường packageManager vẫn hữu ích để đồng bộ môi trường local.
Thêm header bảo mật cơ bản
Tạo public/_headers:
/*
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: DENY
Permissions-Policy: camera=(), microphone=(), geolocation=()
/admin/*
X-Robots-Tag: noindex, nofollow
Cache-Control: no-store
Cloudflare Pages đọc file này và áp header cho site tĩnh. Đây chỉ là lớp bảo vệ cơ bản; với trang quản trị, hãy dùng thêm Cloudflare Access và xác thực của CMS.
Kiểm tra build lần cuối
pnpm build
Nếu dùng Pagefind để tìm kiếm, nhớ chạy nó sau khi Astro build xong:
pnpm build
pnpm dlx pagefind --site dist
Không chạy Pagefind trước astro build, vì lúc đó chưa có HTML để index.
4. Đưa code lên GitHub
Nếu project chưa có Git:
git init
git add .
git commit -m "feat: khoi tao blog Astro"
git branch -M main
git remote add origin https://github.com/<user>/<repo>.git
git push -u origin main
Một vài lưu ý:
- Repo có thể private; Cloudflare Pages vẫn kết nối được nếu bạn cấp quyền cho GitHub App của Cloudflare.
- Luôn commit
pnpm-lock.yamlđể build có dependency cố định. - Không commit
dist/,node_modules/hay file.envchứa secret. - Nếu dùng GitHub Actions, có thể để Pages tự build; không cần thêm workflow trừ khi bạn muốn chạy test trước.
5. Kết nối Cloudflare Pages
Vào Cloudflare Dashboard:
- Chọn Workers & Pages.
- Chọn Create application → Pages → Connect to Git.
- Cấp quyền cho Cloudflare truy cập repo GitHub.
- Chọn repo và nhấn Begin setup.
Điền cấu hình:
| Trường | Giá trị |
|---|---|
| Project name | my-blog (hoặc tên bạn muốn) |
| Production branch | main |
| Framework preset | Astro |
| Build command | pnpm build |
| Build output directory | dist |
| Root directory | / (hoặc thư mục con nếu monorepo) |
Cloudflare có thể tự điền npm run build khi chọn preset Astro. Hãy sửa lại thành pnpm build nếu repo dùng pnpm.
Biến môi trường
Với static site, biến môi trường chỉ có tác dụng tại thời gian build. Nếu cần ghim phiên bản:
NODE_VERSION=24.22.1
PNPM_VERSION=12.15.0
Chỉ đặt những biến thật sự cần. Lưu ý:
- Biến bắt đầu bằng
PUBLIC_trong Astro sẽ bị nhúng vào JavaScript phía client. Không bao giờ đặt API key, token hay mật khẩu vàoPUBLIC_*. - Với site tĩnh, không có server runtime để giấu secret. Nếu cần secret, đó là dấu hiệu bạn cần SSR/Workers hoặc một backend riêng.
- Cloudflare Pages cho phép đặt biến riêng cho Production và Preview; đừng dùng chung secret giữa hai môi trường.
Nhấn Save and Deploy. Theo dõi log build. Khi thành công, bạn nhận được URL dạng:
https://my-blog.pages.dev
Mở URL này và kiểm tra vài trang trước khi gắn domain.
Cách khác: deploy trực tiếp bằng Wrangler
Nếu chưa muốn nối GitHub, có thể build ở local rồi upload:
pnpm build
pnpm dlx wrangler pages deploy dist --project-name my-blog
Cách này nhanh nhưng không có tự động build khi push. Với blog cần cập nhật thường xuyên, nối Git vẫn tiện hơn.
6. Gắn tên miền riêng
Trong project Pages:
- Vào Custom domains.
- Thêm domain gốc, ví dụ
example.com. - Thêm cả
www.example.comnếu muốn người dùng gõwwwvẫn vào được. - Làm theo hướng dẫn DNS mà Cloudflare hiển thị.
Nếu domain đang dùng nameserver của Cloudflare, việc tạo bản ghi thường gần như tự động. Nếu DNS nằm ở nơi khác:
- Tạo bản ghi CNAME cho
wwwtrỏ tớimy-blog.pages.dev. - Với domain gốc (apex), dùng ALIAS/ANAME hoặc cơ chế CNAME flattening của nhà cung cấp DNS. Cloudflare hỗ trợ CNAME ở apex.
- Chờ trạng thái custom domain chuyển sang Active và chứng chỉ SSL được cấp.
Đừng vội kết luận site lỗi nếu vừa thêm domain mà thấy 522 hoặc lỗi SSL. DNS và certificate có thể cần vài phút; hãy kiểm tra lại trạng thái trong Pages trước.
Chọn một URL chính thức
Nên chọn một trong hai:
https://example.com
https://www.example.com
rồi chuyển hướng cái còn lại về đó bằng 301. Với Cloudflare, dùng Rules → Redirect Rules:
- Điều kiện: host bằng
www.example.com. - Đích:
https://example.com+ đường dẫn hiện tại. - Bật Preserve query string.
- Dùng mã 301 để chuyển hướng vĩnh viễn.
Kiểm tra:
curl -I "https://www.example.com/posts/ten-bai/?x=1"
Kết quả mong đợi là 301 và Location trỏ về https://example.com/posts/ten-bai/?x=1.
Sau đó đảm bảo site trong astro.config là URL chính thức, để canonical, RSS và sitemap không sinh ra địa chỉ www.
7. Quy trình cập nhật và rollback
Sau khi nối Git, mỗi lần push lên main sẽ kích hoạt build production. Các nhánh khác có thể tạo preview deployment với URL riêng — hữu ích để xem thử trước khi merge.
Một số thói quen nên giữ:
- Push nhỏ, thường xuyên. Build sẽ báo lỗi sớm hơn.
- Dùng preview branch cho thay đổi lớn về layout hoặc config.
- Không sửa trực tiếp file trong
dist/. Đó là output, sẽ bị ghi đè ở lần build sau. - Không commit secret. Nếu lỡ commit, coi như secret đã lộ; rotate ngay, đừng chỉ xóa file.
Bỏ qua build khi cần
Cloudflare Pages bỏ qua build nếu commit message chứa các token như [skip ci]. Blog này dùng [skip ci] cho những commit chỉ sửa tài liệu hoặc lưu bản nháp từ CMS.
Ví dụ:
git commit -m "docs: cap nhat ghi chu [skip ci]"
Chỉ dùng khi thật sự không cần deploy. Nếu bạn đang chờ một thay đổi quan trọng lên production, đừng thêm token này.
Rollback
Trong Pages → Deployments, chọn một deployment production cũ và nhấn Rollback. Cloudflare sẽ phục vụ lại bản build đó.
Lưu ý: rollback chỉ đổi bản đang phục vụ, không sửa repo. Nếu lỗi vẫn còn trong source, lần build tiếp theo sẽ đưa lỗi trở lại. Cách bền vững là revert commit hoặc sửa source rồi deploy lại.
8. Kiểm tra sau khi deploy
Đừng chỉ nhìn trang chủ. Một checklist ngắn:
-
/trả200và giao diện đúng. - Một bài viết thật trả
200, đúng URL và canonical. -
/rss.xmltrả200, có link bài mới. -
/sitemap-index.xmltrả200, URL dùng domain chính thức. - Trang không tồn tại trả
404, không phải trang chủ. - HTTPS hợp lệ, không có cảnh báo chứng chỉ.
-
wwwchuyển301về domain chính, giữ path và query. - Ảnh, CSS, JS tải đúng; không có lỗi 404 trong Console.
- OG image hiển thị đúng khi chia sẻ link.
- Tìm kiếm hoạt động nếu dùng Pagefind.
- Mobile và Lighthouse ổn; ít nhất kiểm tra trang chủ và một bài dài.
- Trang
/admin(nếu có CMS) được bảo vệ, không chỉ dựa vàonoindex.
Có thể kiểm tra nhanh bằng curl:
curl -I https://example.com/
curl -I https://example.com/rss.xml
curl -I https://example.com/sitemap-index.xml
curl -I https://example.com/khong-ton-tai/
9. Lỗi thường gặp
| Hiện tượng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
pnpm: not found | Build image chưa bật pnpm/Corepack | Đặt PNPM_VERSION, thêm bước corepack enable, hoặc dùng npm nếu project không phụ thuộc pnpm |
| Build lỗi phiên bản Node | .nvmrc hoặc NODE_VERSION không khớp | Kiểm tra log; đặt NODE_VERSION đúng major mà Pages hỗ trợ |
| Build thành công nhưng site 404 | Sai output directory hoặc root directory | Đặt output dist, root / (hoặc đúng thư mục con) |
| CSS/JS/ảnh 404 | Sai site, base hoặc trailingSlash | Kiểm tra astro.config; dùng đường dẫn nhất quán; xem URL thực tế trong HTML |
Domain mới báo 522/SSL pending | DNS chưa đúng hoặc certificate chưa cấp | Kiểm tra custom domain status, bản ghi CNAME/ALIAS; chờ thêm |
| Push rồi nhưng nội dung không đổi | Build bị skip, sai branch hoặc đang xem cache | Xem commit message có [skip ci] không; kiểm tra deployment mới nhất; hard-refresh |
| RSS/sitemap trỏ về localhost | Thiếu site trong Astro config | Đặt site thành domain thật rồi build lại |
| Tìm kiếm không có kết quả | Pagefind chạy trước Astro build | Chạy astro build trước, sau đó mới index dist |
Redirect www bị vòng lặp | Rule redirect ngược hoặc sai SSL/TLS mode | Kiểm tra rule một chiều www → apex; không tạo rule ngược lại |
| Biến môi trường không có trong browser | Biến không phải PUBLIC_*, hoặc site tĩnh | Chỉ dùng PUBLIC_* cho giá trị công khai; secret phải xử lý ở backend |
Một lỗi ít được chú ý: đừng thêm fallback SPA kiểu /* /index.html 200 cho một site Astro nhiều trang. Nó sẽ biến mọi URL sai thành trang chủ và làm hỏng SEO/404. Astro đã tạo sẵn các file HTML tĩnh; không cần fallback trừ khi bạn thật sự xây SPA.
10. Khi nào cần Workers hoặc SSR?
Với blog tĩnh, Cloudflare Pages là đủ. Bạn chỉ cần chuyển sang hướng Workers/SSR khi:
- Có trang cần dữ liệu thay đổi theo từng request.
- Cần API routes, authentication phía server, webhook hoặc xử lý form.
- Muốn dùng Cloudflare bindings (D1, KV, R2, AI) trực tiếp trong Astro.
Khi đó, thêm adapter Cloudflare của Astro, chuyển output sang chế độ server và deploy qua Workers thay vì chỉ upload file tĩnh. Secret lúc này nên đặt bằng wrangler secret hoặc trong dashboard, không nằm trong biến PUBLIC_*.
Đừng thêm adapter SSR chỉ vì “cho hiện đại”. Static site build nhanh, ít lỗi runtime, ít chi phí và dễ cache hơn.
11. Bảo mật và chi phí
Một blog tĩnh trên Cloudflare Pages thường nằm gọn trong gói Free:
- Không có server thường trực để vá bảo mật.
- Nội dung được phục vụ qua CDN.
- Chi phí chủ yếu đến từ domain và dung lượng media nếu bạn dùng ngoài.
Vài nguyên tắc:
- Không đưa secret vào repo. Kể cả repo private, secret vẫn nằm trong lịch sử Git.
- Dùng Cloudflare Access cho
/adminvà các trang quản trị;noindexkhông phải bảo mật. - Bật 2FA cho GitHub và Cloudflare.
- Ghim dependency và cập nhật qua branch/preview, không sửa thẳng production.
- Backup nội dung và media; rollback deployment không phải backup.
- Theo dõi build failure để không phát hiện site chết sau nhiều ngày.
12. Áp dụng cho blog này
Blog này dùng AstroPaper với Astro, pnpm và Pagefind. Cấu hình thực tế:
Node: 24.22.1
pnpm: 12.15.0
Build command: pnpm build
Output: dist
Domain chính: https://phamnam.me
www: 301 → https://phamnam.me
Script pnpm build trong repo không chỉ chạy astro build mà còn kiểm tra type, tạo index tìm kiếm Pagefind và copy asset cần thiết. Vì vậy trên Cloudflare Pages chỉ cần đúng một lệnh pnpm build; không cần thêm bước build thủ công.
Trang quản trị /admin/ được đặt sau Cloudflare Access, và CMS có lớp đăng nhập GitHub riêng. Đây là mô hình hai lớp: Access chặn truy cập ở edge, CMS kiểm tra quyền ghi repo. Không lớp nào thay thế lớp còn lại.
Tóm lại
Quy trình ngắn gọn:
- Tạo Astro project, chạy
pnpm buildvà kiểm tradist/. - Đặt
site, ghim Node/pnpm, commit lockfile. - Push lên GitHub.
- Nối repo với Cloudflare Pages; build
pnpm build, outputdist. - Gắn domain, bật HTTPS, chọn URL chính và redirect
wwwbằng 301. - Kiểm tra RSS, sitemap, 404, canonical và mobile sau mỗi lần deploy.
Cloudflare Pages không làm blog của bạn “tự động hay hơn”, nhưng nó lo phần vận chuyển rất tốt: build từ Git, CDN toàn cầu, HTTPS và rollback đều có sẵn. Phần còn lại — nội dung, cấu trúc và trải nghiệm đọc — vẫn là việc của bạn.