TL;DR
- Workers Assets gom static hosting và Worker runtime vào một đơn vị triển khai — không còn phải kết hợp Pages + Pages Functions + Worker phụ cho route.
- Cấu hình tối thiểu
wrangler.jsonc:"assets": { "directory": "./dist", "not_found_handling": "404-page" }— sau đónpm run build && wrangler deploy.- Lỗi nguy hiểm nhất: không dùng
not_found_handling: "single-page-application"cho blog — nó rewrite URL không tồn tại vềindex.htmlvới HTTP 200, phá SEO, broken-link check, RSS và analytics.- Asset có hash (
/assets/index.B7a1c4.js) cache lâu; file không hash (rss.xml,sitemap.xml,robots.txt,og-default.png) cầnCache-Controlngắn hơn qua_headers— kiểm tra bằngcurl -Isau deploy.- Chọn Workers Assets khi site sẽ cần route API, logic redirect, header tuỳ chỉnh hoặc preview URL; giữ Pages cho site tĩnh thuần kết thúc ở git push.
- Thay đổi mô hình: từ “static hosting có function tuỳ chọn” sang “edge lập trình được có assets gắn kèm”.
Cloudflare Pages từng là lựa chọn mặc định nếu bạn muốn host một blog tĩnh: đẩy code lên Git, build, triển khai, gắn domain, xong.
Nhưng khi site bắt đầu cần thêm một chút logic, route API, URL preview, redirect có điều kiện, header tuỳ chỉnh, middleware nhẹ, Pages bắt đầu không còn là mô hình gọn nhất nữa. Bạn vẫn làm được, nhưng thường phải kéo thêm Worker bên cạnh, rồi quản lý route, triển khai và giám sát ở hai nơi khác nhau.
Đó là lý do tôi chuyển blog này sang Cloudflare Workers Assets.
Bài này ghi lại lý do chuyển, cấu hình tối thiểu, và vài điểm nhỏ nhưng dễ làm bạn mất thời gian truy nguyên nếu chưa biết trước.
Workers Assets là gì?
Trước đây, Workers mạnh ở phần tính toán tại edge, còn Pages mạnh ở phần static hosting.
Nếu muốn phục vụ file tĩnh bằng Worker, bạn thường phải tự viết logic đọc file từ KV, R2 hoặc một backend khác. Làm được, nhưng không tự nhiên nếu mục tiêu chỉ là host một blog tĩnh.
Workers Assets giải quyết đúng khoảng trống đó: bạn có thể triển khai thư mục đầu ra build, ví dụ dist/, như static assets gắn trực tiếp với Worker.
Nói ngắn gọn:
- Pages cho bạn static hosting rất nhanh và đơn giản.
- Workers cho bạn môi trường chạy edge rất linh hoạt.
- Workers Assets gom hai thứ đó vào cùng một đơn vị triển khai.
Với blog tĩnh, khác biệt lớn nhất không nằm ở việc HTML được phục vụ nhanh hơn bao nhiêu. Khác biệt nằm ở chỗ bạn có thể bắt đầu từ static site, nhưng vẫn có đường mở rộng rất tự nhiên khi cần thêm logic.
Vì sao tôi chuyển từ Pages sang Workers Assets
Blog này ban đầu chỉ cần static hosting. Với trường hợp sử dụng đó, Pages hoàn toàn ổn.
Nhưng sau một thời gian, tôi bắt đầu muốn có thêm vài thứ:
- URL preview cho bài draft.
- Luật redirect linh hoạt hơn.
- Response header tuỳ chỉnh.
- API nhỏ cho vài tính năng phụ.
- Một chỗ duy nhất để quan sát log và lỗi.
- Một luồng triển khai nhất quán với các Worker khác.
Nếu tiếp tục dùng Pages, tôi vẫn có thể xử lý bằng Pages Functions hoặc một Worker riêng. Nhưng khi site đã đi theo hướng cần logic chạy động, Workers Assets trở thành mô hình sạch hơn.
Thay vì nghĩ theo kiểu:
Pages build static site
+ Pages Functions
+ Worker riêng cho một vài route
+ route config trên zone
tôi muốn đưa nó về:
Worker
+ static assets
+ route logic
+ observability
+ custom domain
Một đơn vị triển khai. Một mô hình môi trường chạy. Một nơi để debug.
Cấu hình tối thiểu
Đây là cấu hình wrangler.jsonc cho blog:
{
"name": "khavan",
"compatibility_date": "2025-10-08",
"compatibility_flags": ["nodejs_compat"],
"assets": {
"directory": "./dist",
"not_found_handling": "404-page"
}
}
Sau khi build site, chỉ cần triển khai:
npm run build
wrangler deploy
wrangler sẽ upload nội dung trong dist/ thành assets của Worker.
Nếu site của bạn chỉ là HTML/CSS/JS tĩnh, script Worker có thể rất tối giản, thậm chí gần như không cần viết logic gì thêm. Nhưng điểm quan trọng là: khi cần logic, bạn đã ở sẵn trong môi trường chạy Worker.
Điểm tôi thích nhất
1. Một đơn vị triển khai duy nhất
Với Pages, static site và logic Worker thường tạo cảm giác như hai phần riêng biệt. Khi có lỗi, bạn phải tự hỏi lỗi nằm ở build, Pages, Functions, Worker route hay cache.
Với Workers Assets, static assets và logic chạy động đi cùng nhau trong một bản triển khai.
Điều này đặc biệt hữu ích khi bạn muốn phát hành các thay đổi có liên quan với nhau, ví dụ:
- Cập nhật trang tĩnh.
- Thêm redirect mới.
- Thêm route API.
- Thay đổi response header.
- Cập nhật hành vi cache.
Tất cả cùng nằm trong một phiên bản triển khai, dễ khôi phục và dễ kiểm soát hơn.
2. Routing trở thành code
Static site thường bắt đầu đơn giản, nhưng không phải lúc nào cũng đơn giản mãi.
Một blog có thể dần cần:
/api/*cho tính năng nội bộ nhỏ./preview/*cho nội dung draft.- Redirect URL cũ sang URL mới.
- Logic riêng cho đường dẫn theo ngôn ngữ.
- Header riêng cho RSS, sitemap, ảnh OpenGraph hoặc static assets.
- Bảo vệ cơ bản cho một số route chưa công khai.
Khi dùng Workers Assets, các logic này có thể được viết ngay trong Worker thay vì tách sang một dịch vụ khác.
Đây là điểm khác biệt lớn: bạn không chỉ host file tĩnh, bạn có một lớp edge lập trình được ngay phía trước file tĩnh.
3. Giám sát tốt hơn cho vận hành
Với blog nhỏ, giám sát nghe có vẻ hơi quá mức. Nhưng khi có lỗi thật, log luôn đáng tiền.
Workers cho bạn log lúc chạy, invocation, tỉ lệ lỗi và luồng truy nguyên quen thuộc hơn. Nếu sau này có route API hoặc middleware, bạn không phải dựng thêm cách quan sát riêng.
Một blog tĩnh có thể không cần nhiều giám sát. Nhưng một site có traffic thật, SEO thật, redirect thật và tích hợp thật thì rất nên có khả năng truy nguyên rõ ràng.
4. Custom domain vẫn đơn giản
Phần custom domain không có gì đặc biệt khó.
Bạn thêm domain vào Worker, Cloudflare xử lý certificate, rồi định tuyến traffic về Worker. Với người đã quen Cloudflare dashboard hoặc wrangler, luồng này khá thẳng.
Điểm đáng giá là domain đó không chỉ trỏ vào static hosting. Nó trỏ vào một Worker có thể xử lý cả static assets lẫn logic phía trước.
Hai lỗi nhỏ tôi gặp khi cấu hình
1. Dùng nhầm not_found_handling
Đây là lỗi dễ gặp nhất.
Workers Assets hỗ trợ nhiều cách xử lý khi một asset không tồn tại. Nếu chọn:
"not_found_handling": "single-page-application"
thì các URL không tồn tại sẽ được rewrite về index.html với HTTP status 200.
Hành vi này đúng với SPA như React hoặc Vue, nơi mọi route đều do router phía client xử lý.
Nhưng với blog tĩnh, đây là lựa chọn sai.
Ví dụ người dùng truy cập:
/posts/mot-bai-da-bi-xoa/
Nếu URL đó không còn tồn tại, response đúng phải là 404.
Nếu trả về index.html với status 200, bạn sẽ gặp vài vấn đề:
- Công cụ tìm kiếm có thể xem URL lỗi như một trang hợp lệ.
- Công cụ kiểm tra broken link tưởng mọi link đều còn sống.
- Trình đọc RSS hoặc crawler nhận nội dung sai.
- Người dùng không thấy trang 404 rõ ràng.
- Dữ liệu analytics bị nhiễu vì các URL lỗi trông như page view hợp lệ.
Với blog tĩnh, nên dùng:
"not_found_handling": "404-page"
Cấu hình này giúp phục vụ dist/404.html và trả về HTTP 404 thật.
Đây là một dòng config nhỏ, nhưng ảnh hưởng trực tiếp đến SEO, hành vi crawler và chất lượng vận hành site.
2. Header cache cho static assets cần kiểm tra kỹ
Workers Assets tối ưu caching khá tốt, đặc biệt với các file có content hash trong tên file.
Ví dụ:
/assets/index.B7a1c4.js
/assets/post-card.D8s9x2.png
/assets/style.F92kda.css
Các file này an toàn để cache lâu, vì khi nội dung đổi thì tên file cũng đổi. Đây là mẫu chuẩn của hầu hết công cụ sinh static site hiện nay, bao gồm Astro.
Nhưng không phải file nào trong site cũng có hash.
Ví dụ:
/og-default.png
/favicon.png
/robots.txt
/sitemap.xml
/rss.xml
Những file này cần chính sách cache khác nhau.
favicon.pngcó thể cache lâu.og-default.pngcó thể cần cập nhật khi đổi nhận diện thương hiệu.robots.txtkhông nên bị cache quá cứng nếu bạn còn điều chỉnh luật indexing.sitemap.xmlvàrss.xmlnên phản ánh nội dung mới tương đối nhanh.
Vì vậy sau khi triển khai, nên kiểm tra header thực tế:
curl -I https://example.com/og-default.png
curl -I https://example.com/rss.xml
curl -I https://example.com/sitemap.xml
Nếu một file không có content hash nhưng lại bị cache quá lâu, có thể ghi đè bằng _headers.
Ví dụ:
/og-default.png
Cache-Control: public, max-age=3600
/rss.xml
Cache-Control: public, max-age=300
/sitemap.xml
Cache-Control: public, max-age=300
/robots.txt
Cache-Control: public, max-age=3600
Cách làm tốt nhất là:
- File đầu ra build có hash: cho cache lâu.
- File metadata, feed, sitemap: cache ngắn hơn.
- File nhận diện thương hiệu ít đổi: cache trung bình hoặc dài tuỳ nhu cầu.
- Khi không chắc: kiểm tra response header thực tế sau khi triển khai.
Khi nào nên chọn Workers Assets?
Tôi sẽ chọn Workers Assets nếu site có một trong các nhu cầu sau:
- Static site nhưng có khả năng cần route API sau này.
- Muốn viết logic redirect hoặc định tuyến bằng code.
- Cần header tuỳ chỉnh, luật cache hoặc middleware nhẹ.
- Muốn có mô hình triển khai gần với Workers.
- Muốn gom static assets và logic edge vào một chỗ.
- Đã quen dùng
wrangler. - Muốn kiểm soát nhiều hơn Pages nhưng không muốn tự dựng static hosting.
Nói cách khác: nếu bạn đang xây một static site nhưng biết rằng nó sẽ không chỉ là static mãi, Workers Assets là lựa chọn rất hợp lý.
Khi nào vẫn nên dùng Pages?
Pages vẫn là lựa chọn tốt nếu bạn muốn đơn giản tuyệt đối.
Ví dụ:
- Blog cá nhân rất đơn giản.
- Landing page.
- Site tài liệu nhỏ.
- Nhóm không quen Workers.
- Chỉ cần Git push là build và triển khai.
- Không có nhu cầu chạy logic động riêng.
- Không muốn nghĩ về Worker, route hay
wrangler.
Pages có lợi thế lớn ở bước làm quen. Người mới dùng Cloudflare có thể hiểu Pages rất nhanh. Với một site tĩnh thuần tuý, Pages vẫn làm rất tốt nhiệm vụ của nó.
Vì vậy, tôi không nghĩ Workers Assets “giết chết” Pages. Hai mô hình này phục vụ hai mức nhu cầu khác nhau.
Pages phù hợp khi bạn muốn static hosting đơn giản.
Workers Assets phù hợp khi bạn muốn static hosting nhưng vẫn giữ quyền mở rộng bằng edge lập trình được.
Đánh đổi thực tế
Sau khi chuyển, cảm giác lớn nhất là mô hình vận hành gọn hơn.
Nhưng cũng có vài đánh đổi cần chấp nhận:
| Tiêu chí | Pages | Workers Assets |
|---|---|---|
| Bước làm quen | Dễ hơn | Cần hiểu Workers/Wrangler |
| Static hosting | Rất tốt | Rất tốt |
| Logic API/chạy động | Có, nhưng không phải trọng tâm | Tự nhiên hơn |
| Định tuyến bằng code | Hạn chế hơn | Mạnh hơn |
| Giám sát | Đủ cho static site | Tốt hơn khi có logic |
| Mô hình triển khai | Xoay quanh Git | Xoay quanh Worker |
| Phù hợp cho người mới | Rất phù hợp | Hơi kỹ thuật hơn |
Nếu chỉ cần host blog đơn giản, khác biệt này có thể không đáng để chuyển đổi.
Nhưng nếu bạn đã bắt đầu nghĩ đến route API, preview, middleware, cache riêng hoặc logic redirect, Workers Assets sẽ cho bạn nhiều không gian hơn.
Danh sách kiểm tra trước khi triển khai blog bằng Workers Assets
Nếu làm lại từ đầu, tôi sẽ kiểm tra nhanh các điểm này:
dist/404.htmlđã tồn tại chưa.not_found_handlingcó đang là404-pagekhông.- URL sai có trả HTTP
404thật không. - RSS, sitemap và robots.txt có header cache hợp lý không.
- Asset có hash có được cache dài không.
- File không có hash có bị cache quá lâu không.
- Custom domain đã định tuyến đúng về Worker chưa.
- Redirect từ URL cũ sang URL mới đã hoạt động chưa.
- Log trên Worker có đủ để truy nguyên lỗi cơ bản chưa.
Chỉ cần danh sách kiểm tra này là tránh được phần lớn lỗi nhỏ khi chuyển đổi từ Pages sang Workers Assets.
Điều rút ra
Workers Assets không phải là “Pages phiên bản mới” theo nghĩa thay thế tuyệt đối. Cách hiểu đúng hơn là: Workers Assets đưa static hosting vào trong môi trường chạy Worker.
Với một blog tĩnh, điều này có vẻ nhỏ. Nhưng về mặt kiến trúc, nó thay đổi khá nhiều.
Bạn không còn bị giới hạn ở mô hình static hosting thuần. Bạn có thể bắt đầu rất đơn giản, rồi thêm logic dần khi site lớn hơn mà không phải đổi nền tảng triển khai.
Với tôi, đó là lý do đủ thuyết phục để chuyển blog sang Workers Assets.
Kết
Nếu bạn đang chạy Cloudflare Pages và mọi thứ ổn, không cần chuyển đổi chỉ để chạy theo công nghệ mới.
Nhưng nếu bạn đang bắt đầu một dự án mới, hoặc site của bạn đã bắt đầu cần route API, URL preview, logic redirect, header tuỳ chỉnh hay giám sát tốt hơn, tôi sẽ chọn Workers Assets ngay từ đầu.
Một blog tĩnh có thể bắt đầu bằng HTML, CSS và RSS. Nhưng khi nó lớn dần, có một lớp edge lập trình được phía trước sẽ giúp bạn đi xa hơn mà không phải thiết kế lại từ đầu.