Cloudflare Image Optimization: Polish, Image Resizing, và Workers - tối ưu ảnh không cần thay đổi code
Ảnh thường chiếm 60–80% tổng bytes của một web page. Cloudflare có 3 tầng tối ưu ảnh: **Polish** (nén tự động, không cần code), **Image Resizing** (resize theo URL params, Pro plan), và **Transform via Workers** (full control, dùng Workers). Bài này hướng dẫn cả ba - từ setup nhanh nhất đến pipeline on-the-fly linh hoạt nhất. ---
Vấn đề
Tình huống quen thuộc: designer export ảnh banner 3000×2000px, 2.5MB. Đưa lên web không xử lý. User mobile tải nguyên file đó để hiển thị trong container 375px width. Lãng phí bandwidth, chậm load, trải nghiệm kém - trong khi ảnh đúng chuẩn chỉ cần 80KB.
Giải pháp truyền thống: setup ImageMagick/Sharp trên server, viết pipeline resize, lưu nhiều kích thước. Cloudflare làm điều này ở edge - không cần server, không cần code xử lý ảnh, không cần lưu nhiều phiên bản.
3 tầng image optimization của Cloudflare
Tầng 1: Polish → Nén tự động (free plan: lossless, Pro: lossy + WebP)
Tầng 2: Image Resizing → Resize theo URL params (Pro plan)
Tầng 3: Workers Transform → Full pipeline qua Workers (Pro plan)
Tùy nhu cầu, bạn dùng một hoặc kết hợp cả ba.
Tầng 1: Polish - nén ảnh tự động, không cần code (Free + Pro)
Polish là tính năng đơn giản nhất: Cloudflare tự động nén và tối ưu ảnh khi serve từ cache, không cần thay đổi gì trong code hay markup.
Bật Polish
Vào Speed → Optimization → Image Optimization → Polish.
Có 3 chế độ:
| Chế độ | Mô tả | Plan |
|---|---|---|
| Off | Không làm gì | All |
| Lossless | Nén không mất chất lượng, xóa metadata (EXIF) | Free |
| Lossy | Nén có mất chất lượng nhẹ, convert WebP | Pro |
Kết quả thực tế:
- Lossless: giảm trung bình ~21% file size
- Lossy: giảm 30–50%, đặc biệt hiệu quả với JPEG
- WebP (Lossy): PNG giảm ~26%, JPEG giảm ~17% so với không WebP
Bật WebP (kèm theo Lossy)
Vào Speed → Optimization → Image Optimization → Polish → bật thêm toggle WebP.
Khi WebP được bật, Cloudflare phục vụ WebP cho browser hỗ trợ (gần như tất cả browser hiện đại), giữ JPEG/PNG cho browser cũ - tự động dựa trên Accept header.
Request (Chrome): Accept: image/webp,image/apng,...
→ Cloudflare serve: image.webp
Request (IE11): Accept: image/jpeg,...
→ Cloudflare serve: image.jpg (original)
Không cần thay đổi HTML hay tag - Cloudflare xử lý hoàn toàn transparent.
Lưu ý quan trọng với Polish
- Polish chỉ áp dụng cho ảnh được cache ở Cloudflare (proxy ON). Nếu Cache-Control là
no-store, Polish không chạy. - Polish không áp dụng cho ảnh đã được transform qua Image Resizing - resized images đã được tối ưu riêng.
- Polish áp dụng cho: JPEG, PNG, GIF, WebP nguồn. Không áp dụng: SVG, animated GIF phức tạp.
- Verify Polish đang hoạt động: xem response header
cf-polishedtrên ảnh.
curl -I https://yourdomain.com/images/banner.jpg | grep cf-polished
# cf-polished: qual=85, origFmt=jpeg, origSize=245678
Tầng 2: Image Resizing - resize on-the-fly qua URL (Pro plan)
Image Resizing cho phép resize, crop, convert format ảnh bằng cách thêm params vào URL - không cần lưu nhiều phiên bản trên server.
Cú pháp URL
https://yourdomain.com/cdn-cgi/image/<options>/<original-url>
Ví dụ:
# Resize width 800px, giữ ratio, convert WebP
https://example.com/cdn-cgi/image/width=800,format=webp/uploads/banner.jpg
# Resize 400x300, crop giữa
https://example.com/cdn-cgi/image/width=400,height=300,fit=crop/uploads/product.jpg
# Chất lượng 80%, nén
https://example.com/cdn-cgi/image/quality=80,format=auto/uploads/photo.jpg
Các options phổ biến
| Option | Mô tả | Ví dụ |
|---|---|---|
width |
Chiều rộng (px) | width=800 |
height |
Chiều cao (px) | height=600 |
fit |
Cách fit: scale-down, contain, cover, crop, pad |
fit=cover |
quality |
Chất lượng 1-100 | quality=85 |
format |
Output format: webp, avif, jpeg, png, auto |
format=auto |
dpr |
Device pixel ratio (retina) | dpr=2 |
blur |
Blur ảnh (1-250) | blur=10 |
sharpen |
Tăng độ sắc nét (0-10) | sharpen=2 |
gravity |
Điểm neo khi crop: auto, left, right, top, bottom, face |
gravity=auto |
format=auto tự động chọn WebP hoặc AVIF nếu browser hỗ trợ - khuyến nghị dùng mặc định.
Dùng với ![]()
srcset (responsive images)
<img
src="/cdn-cgi/image/width=800,format=auto/uploads/hero.jpg"
srcset="
/cdn-cgi/image/width=400,format=auto/uploads/hero.jpg 400w,
/cdn-cgi/image/width=800,format=auto/uploads/hero.jpg 800w,
/cdn-cgi/image/width=1200,format=auto/uploads/hero.jpg 1200w
"
sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
alt="Hero banner"
loading="lazy"
/>
Browser tự chọn kích thước phù hợp với viewport và pixel density - không cần JavaScript.
Tầng 3: Transform qua Workers - full control (Pro plan)
Nếu cần logic phức tạp hơn (thêm watermark, dynamic crop dựa trên content, A/B test format), dùng Workers để gọi Image Resizing API:
// Worker: image-optimizer.ts
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
// Chỉ xử lý path /img/*
if (!url.pathname.startsWith("/img/")) {
return fetch(request);
}
// Lấy original image từ R2 hoặc origin
const originalPath = url.pathname.replace("/img/", "/originals/");
const originalUrl = `https://${url.hostname}${originalPath}`;
// Parse resize params từ query string
const width = parseInt(url.searchParams.get("w") ?? "800");
const quality = parseInt(url.searchParams.get("q") ?? "85");
const format = url.searchParams.get("fmt") ?? "auto";
// Gọi Image Resizing API của Cloudflare
const response = await fetch(originalUrl, {
cf: {
image: {
width,
quality,
format,
fit: "scale-down",
metadata: "none", // xóa EXIF
},
},
});
if (!response.ok) {
return new Response("Image not found", { status: 404 });
}
// Cache kết quả 30 ngày
const optimizedResponse = new Response(response.body, response);
optimizedResponse.headers.set("Cache-Control", "public, max-age=2592000");
optimizedResponse.headers.set("Vary", "Accept");
return optimizedResponse;
},
};
URL dùng với Worker này:
https://example.com/img/uploads/product.jpg?w=400&q=80&fmt=webp
Tích hợp R2 + Image Resizing
Pattern phổ biến: lưu ảnh gốc trên R2, serve ảnh đã optimize qua Workers:
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const key = url.pathname.slice(1); // "/products/abc.jpg" → "products/abc.jpg"
// Lấy từ R2
const object = await env.IMAGES_BUCKET.get(key);
if (!object) return new Response("Not found", { status: 404 });
// Chuyển R2 object thành URL để Cloudflare Image Resizing xử lý
// (Dùng R2 public URL hoặc presigned URL)
const r2Url = `https://assets.example.com/${key}`;
const width = parseInt(url.searchParams.get("w") ?? "0") || undefined;
const format = url.searchParams.get("fmt") as "webp" | "avif" | "auto" ?? "auto";
return fetch(r2Url, {
cf: {
image: { width, format, quality: 85, fit: "scale-down" },
},
});
},
};
So sánh 3 phương án
| Polish | Image Resizing | Workers Transform | |
|---|---|---|---|
| Plan | Free (lossless) / Pro (lossy+WebP) | Pro | Pro |
| Cần code? | Không | Không (URL params) | Có (Worker) |
| Flexibility | Thấp | Trung bình | Cao |
| Dùng khi | Site không muốn thay đổi code | Cần nhiều kích thước | Logic phức tạp |
| Format output | WebP tự động | WebP, AVIF, auto | WebP, AVIF, auto |
| Retina/DPR | Không | Có (dpr=2) |
Có |
| Cache | Theo Cache-Control | 1 giờ + | Tùy cấu hình |
Best practices tổng hợp
1. Luôn dùng format=auto thay vì chỉ định WebP cứng - Cloudflare tự serve AVIF (nhỏ hơn WebP 30%) cho browser hỗ trợ.
2. Đặt loading="lazy" trên ảnh below the fold - giảm LCP time, không liên quan đến Cloudflare nhưng pair tốt.
3. Xóa EXIF metadata (metadata: "none" trong Workers hoặc Polish làm tự động) - giảm file size, bảo vệ privacy.
4. Lưu ảnh gốc resolution cao nhất trên R2, serve nhiều kích thước qua Image Resizing - không cần lưu nhiều phiên bản.
5. Cache ảnh đã transform lâu (Cache-Control: public, max-age=2592000) - nội dung đã hash trong URL không bao giờ stale.
6. Verify với Lighthouse - sau khi setup, chạy PageSpeed Insights để xem điểm "Serve images in next-gen formats" và "Properly size images" có pass không.
Kết
Cloudflare Image Optimization là một trong những quick win dễ implement nhất để cải thiện performance web - đặc biệt Polish, chỉ cần một click bật là giảm ngay 20–50% dung lượng ảnh. Với site Pro plan, Image Resizing cho phép xây dựng responsive image pipeline hoàn chỉnh mà không cần server riêng để xử lý ảnh.
Kết hợp với Cache Rules và R2 Storage, bạn có một pipeline phân phối ảnh hoàn chỉnh ở edge - nhanh, rẻ, không egress fee.
Tham khảo
- Cloudflare Images - Overview
- Cloudflare Polish
- Transform images
- Transform via Workers
- Optimizing image delivery with R2
BKGlobal Tech Team