Data URI ảnh/font Base64 — khi nào nên nhúng, khi nào tránh (payload & cache)
Data URI = ảnh/font nằm trong HTML/CSS dưới dạng Base64 — bỏ được 1 HTTP request, nhưng phình ~33%, không cache riêng, và dễ làm HTML/CSS “phình” trên mọi page. Nên: icon/SVG nhỏ, critical CSS, email HTML, demo 1 file. Không nên: ảnh lớn, logo lặp lại, font đầy đủ, bất kỳ payload làm First Contentful Paint chậm trên mobile. Encode/decode thử trên công cụ Base64 Kawa (chạy local trên trình duyệt) trước khi dán vào production.
Bạn copy ảnh PNG vào CSS dạng background: url(data:image/png;base64,iVBOR...) — Lighthouse báo payload quá lớn, hoặc HTML email trên Outlook bị cắt. Hoặc font WOFF2 nhúng Base64 làm CSS 80KB trong khi bản URL chỉ 55KB và cache được. Bài này không dạy “Base64 là gì” chung chung; tập trung Data URI ảnh/font inline: khi nào nên, khi nào không — góc payload và cache mà team frontend / outsourcing hay bỏ qua đến lúc production chậm.
Ai nên đọc
- Frontend nhúng icon vào component React/Vue hoặc Tailwind CSS
- Dev email marketing (HTML template không host được CDN ổn định)
- Ai đang cân nhắc
@font-facevớiurl(data:font/woff2;base64,...) - Dev đo Lighthouse / WebPageTest và thấy HTML/CSS phình bất thường
Data URI là gì (nhanh, đúng)
Data URI (hay gọi Data URL) dùng scheme data: để nhúng nội dung trực tiếp vào tài liệu:
data:[<mediatype>][;base64],<data>
Ví dụ ảnh PNG:
<img
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
alt="pixel"
width="1"
height="1"
/>
Ví dụ CSS background:
.icon-check {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZmlsbD0iIzIyYyIgcWQ9Ik0yIDEwbDMgMyA3LTciLz48L3N2Zz4=");
}
Base64 ở đây chỉ là lớp biểu diễn text của byte ảnh/font — không phải mã hóa bảo mật. Ai có chuỗi đều decode được.
Cần encode ảnh → chuỗi nhanh (không upload server lạ): mở Base64 Encode/Decode, chọn ảnh hoặc dán bytes, copy phần sau dấu phẩy rồi ghép prefix data:image/png;base64,.
Vì sao payload phình ~33%
Base64 nhóm 3 byte → 4 ký tự (bảng A–Z a–z 0–9 + / và padding =). Công thức gần đúng:
| File gốc | Base64 ước tính | Ghi chú |
|---|---|---|
| 1 KB | ~1.33 KB | icon rất nhỏ |
| 10 KB | ~13.3 KB | SVG/PNG tối ưu |
| 50 KB | ~66.7 KB | logo / screenshot nhỏ |
| 500 KB | ~665 KB | không nên nhúng HTML |
Thêm prefix data:image/png;base64, (vài chục byte) — không đáng kể so với thân Base64.
Hệ quả thực tế trên mobile Việt Nam (3G/4G không ổn định):
- HTML 200KB + Data URI 400KB → document lớn, parse chậm hơn file ảnh riêng 300KB (vì ảnh riêng có thể progressive + cache)
- Gzip/Brotli nén chuỗi Base64 kém hơn binary PNG/JPEG gốc (entropy cao hơn text có pattern)
- DevTools “Transfer size” của document HTML tăng — dễ nhầm là “API chậm” trong khi thật ra là inline asset
Cache: điểm hay bị bỏ qua nhất
| Tiêu chí | URL tĩnh (/assets/icon.png) | Data URI (Base64 trong HTML/CSS) |
|---|---|---|
| HTTP request | Thêm 1 request (có thể HTTP/2 multiplex) | 0 request riêng |
| Cache trình duyệt | Cache theo URL + Cache-Control/ETag | Đi theo document/CSS — đổi HTML = tải lại chuỗi |
| CDN / edge | Cache asset độc lập, invalidate riêng | Khó cache riêng; gắn với HTML |
| Kích thước wire | Binary gốc (+ gzip tốt hơn) | +~33% Base64, nén kém hơn |
| Tái dùng nhiều trang | 1 lần tải, các trang sau hit cache | Mỗi page HTML mang lại chuỗi (hoặc CSS chung vẫn phình) |
| Phù hợp | Logo, ảnh nội dung, font, banner | Icon nhỏ, critical, email, offline 1 file |
Khi cache “thắng” Data URI
Logo site dùng trên mọi trang: URL /logo.svg tải 1 lần, các navigation sau hit disk/memory cache. Nếu nhúng Base64 vào layout HTML, mỗi document mang theo ~logo×1.33 — đặc biệt tệ với SSR/SSG nhiều trang.
Font cũng vậy: WOFF2 trên CDN với Cache-Control: max-age=31536000, immutable là chuẩn. Data URI font = mất immutable cache + phình CSS.
Khi Data URI “thắng” request
- Icon 1×1 hoặc checkmark dưới ~1–2KB trong critical CSS (tránh FOUC)
- Email HTML: nhiều client chặn ảnh ngoài; inline nhỏ ổn định hơn
- Widget nhúng 1 file HTML gửi khách (không phụ thuộc host ảnh)
Ảnh: quy tắc quyết định
Nên nhúng Data URI
- SVG/icon ≤ ~2–4KB sau tối ưu (SVGO), dùng 1–2 chỗ critical
- Pixel tracking / spacer cực nhỏ trong email
- Prototype / Storybook / gist — ưu tiên 1 file, chưa quan tâm perf production
- Không có origin ảnh (PDF-to-HTML tạm, export offline)
Không nên nhúng
- Hero, banner, ảnh sản phẩm, screenshot bug (> ~8–10KB đã cân nhắc kỹ; > 30KB gần như luôn sai)
- Ảnh dùng lại trên list/grid nhiều item (N lần × Base64 = thảm họa)
- Ảnh người dùng upload (avatar) — luôn URL + object storage
- Khi cần
srcset/ WebP/AVIF theo device — Data URI khó responsive tốt
Case study ngắn
Team A (đúng): Icon check 800 byte SVG → Data URI trong component button. 0 request, HTML tăng không đáng kể.
Team B (sai): Hero 420KB JPEG → Base64 trong index.html. Document ~560KB. Trên 4G chậm, LCP kém. Sửa: URL /hero.webp + fetchpriority="high" + kích thước phù hợp — LCP giảm rõ.
Font: hầu như đừng dùng Data URI
/* Không khuyến nghị cho production web */
@font-face {
font-family: "Brand";
src: url("data:font/woff2;base64,d09GMgAB...") format("woff2");
font-display: swap;
}
Vấn đề:
- Font thường lớn hơn icon nhiều lần
- Không cache riêng → mỗi CSS bundle mang font
- Khó subset động theo trang (tiếng Việt cần nhiều glyph hơn Latin)
Nên:
@font-face {
font-family: "Brand";
src: url("/fonts/brand-subset-vi.woff2") format("woff2");
font-display: swap;
unicode-range: U+0020-007E, U+00C0-024F, U+1EA0-1EF9; /* ví dụ subset */
}
Ngoại lệ hẹp: email client cực kỳ hạn chế; hoặc PDF/HTML offline một lần. Cân nhắc subset vài KB chứ không full family.
Giới hạn trình duyệt & môi trường
- Một số trình duyệt / proxy có giới hạn độ dài Data URI (lịch sử IE rất thấp; modern browser cao hơn nhưng vẫn không phải lý do nhúng MB).
- CSP
img-src/font-srccó thể chặndata:— kiểm tra Content-Security-Policy trước khi ship. - Service Worker cache URL dễ hơn; cache document chứa Data URI khổng lồ tốn storage.
- Email: Gmail/Outlook có giới hạn kích thước message — Base64 ảnh lớn dễ bị cắt hoặc vào spam heuristics.
Checklist trước khi merge PR
- Đo KB gốc → nhân 4/3. Nếu > ~8–10KB và không phải critical icon → dừng, dùng URL.
- Asset tái dùng? → URL + cache.
- Có trong critical path? Icon nhỏ → Data URI OK; ảnh LCP → URL + tối ưu format.
- CSP cho phép
data:? Nếu không → sửa policy hoặc đổi cách nhúng. - Encode đúng MIME:
image/png,image/jpeg,image/svg+xml,image/webp,font/woff2— sai MIME → browser bỏ qua. - Thử decode ngược trên công cụ Base64 để xác nhận chuỗi không thiếu padding
=/ không dính prefix thừa khi chỉ cần thân Base64. - Mobile throttling trong DevTools trước khi merge.
Encode / decode nhanh trên trình duyệt
Quy trình thực tế khi chuẩn bị Data URI:
- Tối ưu ảnh (SVGO / Squoosh) trước khi encode — Base64 không “giảm” chất lượng, chỉ phình thêm.
- Mở Base64 — chạy local trên trình duyệt, không cần đăng ký, phù hợp icon nội bộ / asset chưa public.
- Encode → copy chuỗi → thêm
data:<mime>;base64,phía trước. - Decode khi debug: nếu dán nhầm cả prefix
data:image/...;base64,, nhiều decoder báo invalid character — chỉ lấy phần sau dấu phẩy.
// Browser: file → Data URI (FileReader đã làm sẵn)
function fileToDataURI(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result); // data:...;base64,...
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
// Ước tính kích thước Base64 từ byte length
function estimateBase64Chars(byteLength) {
return Math.ceil(byteLength / 3) * 4;
}
console.log(estimateBase64Chars(30_000)); // ~40000
Phân biệt nhanh với bài “lỗi dấu tiếng Việt”
Nếu bạn decode Base64 ra text tiếng Việt bị mojibake (Xin chà o), đó là vấn đề UTF-8 / atob, không phải Data URI ảnh. Xem bài Base64 decode tiếng Việt bị lỗi dấu. Bài hiện tại chỉ về nhúng binary (ảnh/font) vào URI.
FAQ tóm tắt thao tác
Chỉ cần thân Base64 hay cả Data URI?
Trong thuộc tính src / url() cần full data:...;base64,.... Khi paste vào API chỉ nhận Base64 thuần, bỏ prefix.
SVG có cần Base64 không?
Có thể dùng data:image/svg+xml, + URL-encode (không Base64) cho SVG text — đôi khi ngắn hơn. Base64 vẫn phổ biến khi pipeline đã xuất binary.
Basic Auth Authorization: Basic ... có liên quan?
Cùng dùng Base64 cho user:pass, nhưng không phải Data URI. Đừng nhầm hai use case.
Liên kết liên quan
- Công cụ Base64 Encode/Decode — text, ảnh, xem trước; chạy trên trình duyệt
- Base64 decode tiếng Việt / UTF-8 — khi payload là chữ, không phải ảnh
- Danh sách công cụ