Shared Dictionaries — Khi trình duyệt chỉ tải những gì thực sự thay đổi
Posted on: 4/22/2026 9:12:24 AM
Table of contents
- Bài toán: Tại sao nén truyền thống không còn đủ?
- Gzip → Brotli → Zstandard: Lịch sử nén web
- Shared Dictionaries: Nén delta cho HTTP
- Hiệu năng thực tế: Những con số ấn tượng
- Triển khai trên Server
- Cloudflare Shared Dictionaries: Lộ trình 3 giai đoạn
- Bảo mật: Bài học từ SDCH và giải pháp RFC 9842
- Chiến lược áp dụng thực tế cho dự án 2026
- Tương lai: Từ Web đến Agentic Internet
- Tham khảo
Bài toán: Tại sao nén truyền thống không còn đủ?
Mỗi khi bạn deploy phiên bản mới của JavaScript bundle, dù chỉ sửa một dòng code, toàn bộ file nén phải được truyền lại từ đầu. Gzip và Brotli nén tốt so với bản gốc — nhưng chúng không biết rằng trình duyệt đã có 99% nội dung giống hệt từ lần tải trước.
Với mô hình phát triển hiện đại — CI/CD deploy nhiều lần mỗi ngày, AI-assisted coding tăng tốc độ ship code — vấn đề này ngày càng nghiêm trọng. Ship 10 thay đổi nhỏ mỗi ngày nghĩa là bạn đã vô tình vô hiệu hóa cache 10 lần.
Vấn đề cốt lõi
Nén truyền thống (Gzip, Brotli, Zstd) hoạt động như thể mỗi request là lần đầu tiên. Shared Dictionaries cho phép server nén chỉ phần khác biệt so với phiên bản client đã cache — giống như git diff cho HTTP.
Gzip → Brotli → Zstandard: Lịch sử nén web
Gzip — Chuẩn mực 30 năm
Ra đời năm 1992, sử dụng thuật toán DEFLATE với cửa sổ 32 KB. Gzip vẫn là lựa chọn mặc định của hầu hết web server vì tính tương thích phổ quát. Tuy nhiên, giới hạn dictionary window 32 KB đồng nghĩa với việc nó không tận dụng được các pattern lặp lại ở khoảng cách xa trong file lớn.
Brotli — Bước tiến từ Google
Phát hành năm 2015, Brotli mang theo một built-in dictionary chứa các pattern phổ biến trên web (thẻ HTML, thuộc tính CSS, từ khóa JavaScript). Kết quả là nén tốt hơn Gzip 10-20% cho nội dung web, với cửa sổ lên đến 16 MB. Đến 2026, Brotli được hỗ trợ bởi 98%+ trình duyệt.
Zstandard (Zstd) — Tốc độ và hiệu quả
Được Facebook phát triển và chuẩn hóa qua RFC 8878, Zstandard đạt t��� lệ nén gần bằng Brotli nhưng nhanh hơn đáng kể — nén nhanh hơn 42% với tỷ lệ tương đương. Zstd đặc biệt mạnh khi sử dụng custom dictionary vì kiến trúc của nó được thiết kế từ đầu cho dictionary-based compression.
| Thuật toán | Ra đời | Dictionary Window | Tốc độ nén | Tỷ lệ nén | Browser Support |
|---|---|---|---|---|---|
| Gzip (DEFLATE) | 1992 | 32 KB | Nhanh | Cơ bản | 100% |
| Brotli | 2015 | 16 MB | Chậm hơn Gzip | +10-20% vs Gzip | 98%+ |
| Zstandard | 2016 | Rất lớn | Nhanh hơn Brotli 42% | ≈ Brotli | Chrome 123+, Firefox 126+ |
Shared Dictionaries: Nén delta cho HTTP
Ý tưởng đơn giản nhưng mạnh mẽ: thay vì nén từ con số 0, server sử dụng phiên bản cũ mà client đã cache làm dictionary. Kết quả? Chỉ truyền phần diff — giống hệt cách git lưu trữ thay đổi giữa các commit.
sequenceDiagram
participant B as Trình duyệt
participant S as Server
Note over B,S: Lần tải đầu tiên
B->>S: GET /js/app.bundle.js
S->>B: 200 OK (92 KB Brotli)
Use-As-Dictionary: match="/js/app.bundle.js"
Note over B: Cache file + đánh dấu làm dictionary
Note over B,S: Lần tải sau (file đã thay đổi)
B->>S: GET /js/app.bundle.js
Available-Dictionary: :hash-abc:
Accept-Encoding: br, zstd, dcb, dcz
S->>B: 200 OK (2.6 KB dcz)
Chỉ gửi phần khác biệt!
Note over B: Giải nén bằng dictionary đã cache
Luồng hoạt động của Shared Dictionaries — lần tải sau chỉ truyền delta
Các HTTP Header mới
Use-As-Dictionary — Server gửi header này kèm response, báo cho trình duyệt: "Giữ lại file này vì nó sẽ hữu ích cho lần sau". Header chứa pattern match cho URL mà dictionary sẽ áp dụng.
HTTP/1.1 200 OK
Content-Type: application/javascript
Content-Encoding: br
Use-As-Dictionary: match="/js/app.bundle.js"
Available-Dictionary — Trình duyệt gửi header này trong request tiếp theo, kèm hash SHA-256 của dictionary đã cache, báo cho server: "Đây là những gì tôi đã có".
GET /js/app.bundle.js HTTP/1.1
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fCG4=:
Accept-Encoding: br, zstd, dcb, dcz
dcb / dcz — Hai Content-Encoding mới:
dcb: Dictionary-Compressed Brotlidcz: Dictionary-Compressed Zstandard
Hiệu năng thực tế: Những con số ấn tượng
Case study: YouTube Desktop
| Phương pháp | Kích thước | So với Brotli |
|---|---|---|
| Không nén | 10 MB | — |
| Brotli | 1.8 MB | Baseline |
| Dictionary (deploy cách 2 tháng) | 384 KB | -78% |
| Dictionary (deploy cách 1 tuần) | 172 KB | -90% |
Case study: Amazon Product Pages
| Phương pháp | Kích thước HTML | So với Brotli |
|---|---|---|
| Không nén | 539 KB | — |
| Brotli | 84 KB | Baseline |
| Brotli + Custom Dictionary | 10 KB | -88% |
Khi nào hiệu quả nhất?
Shared Dictionaries đặc biệt mạnh với: (1) JavaScript/CSS bundles deploy thường xuyên — chỉ vài dòng thay đổi mỗi lần, (2) SPA frameworks với code splitting — nhiều chunk chia sẻ cấu trúc giống nhau, (3) WASM applications (Figma, Google Earth) — cải thiện lên đến 95%.
Triển khai trên Server
Node.js với Zstandard Dictionary
const zlib = require('zlib');
const fs = require('fs');
const crypto = require('crypto');
// Tạo dictionary từ phiên bản cũ
const oldBundle = fs.readFileSync('dist/app.v1.js');
const newBundle = fs.readFileSync('dist/app.v2.js');
// Nén KHÔNG dictionary
const normalCompressed = zlib.zstdCompressSync(newBundle);
console.log(`Zstd thường: ${normalCompressed.length} bytes`);
// Nén VỚI dictionary (phiên bản cũ làm dictionary)
const dictCompressed = zlib.zstdCompressSync(newBundle, {
dictionary: oldBundle
});
console.log(`Zstd + dictionary: ${dictCompressed.length} bytes`);
// Tính hash cho Available-Dictionary header
const dictHash = crypto
.createHash('sha256')
.update(oldBundle)
.digest('base64');
console.log(`Dictionary hash: :${dictHash}:`);
Express.js Middleware
const express = require('express');
const app = express();
// Lưu trữ dictionary versions
const dictionaryStore = new Map();
app.get('/js/:file', (req, res) => {
const filePath = `dist/${req.params.file}`;
const content = fs.readFileSync(filePath);
const dictHeader = req.headers['available-dictionary'];
const acceptEnc = req.headers['accept-encoding'] || '';
if (dictHeader && acceptEnc.includes('dcz')) {
// Client có dictionary — gửi delta
const dictHash = dictHeader.replace(/:/g, '');
const dictionary = dictionaryStore.get(dictHash);
if (dictionary) {
const delta = zlib.zstdCompressSync(content, { dictionary });
res.set('Content-Encoding', 'dcz');
res.set('Vary', 'Accept-Encoding, Available-Dictionary');
return res.send(delta);
}
}
// Fallback: nén thường + đánh dấu làm dictionary cho lần sau
const compressed = zlib.brotliCompressSync(content);
const hash = crypto.createHash('sha256').update(content).digest('base64');
dictionaryStore.set(hash, content);
res.set('Content-Encoding', 'br');
res.set('Use-As-Dictionary', `match="/js/${req.params.file}"`);
res.set('Vary', 'Accept-Encoding, Available-Dictionary');
res.send(compressed);
});
Nginx Configuration
# Bật Zstd compression
zstd on;
zstd_comp_level 3;
zstd_types application/javascript application/json text/css;
# Forward Shared Dictionary headers
proxy_set_header Available-Dictionary $http_available_dictionary;
proxy_pass_header Use-As-Dictionary;
# Vary header cho caching đúng
add_header Vary "Accept-Encoding, Available-Dictionary" always;
Cloudflare Shared Dictionaries: Lộ trình 3 giai đoạn
graph LR
subgraph Phase1["Phase 1: Passthrough (Beta 04/2026)"]
A1[Origin tự quản lý dictionary] --> A2[Cloudflare forward headers]
A2 --> A3[Cache key mở rộng
Vary: Available-Dictionary]
end
subgraph Phase2["Phase 2: Managed Dictionaries"]
B1[Admin cấu hình rules] --> B2[Edge inject headers]
B2 --> B3[Delta compress tại edge]
end
subgraph Phase3["Phase 3: Auto Detection"]
C1[Cloudflare phát hiện
URL patterns versioned] --> C2[Tự lưu version cũ]
C2 --> C3[Tự nén delta]
end
Phase1 --> Phase2 --> Phase3
style A1 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style A2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style A3 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style B1 fill:#e94560,stroke:#fff,color:#fff
style B2 fill:#e94560,stroke:#fff,color:#fff
style B3 fill:#e94560,stroke:#fff,color:#fff
style C1 fill:#2c3e50,stroke:#fff,color:#fff
style C2 fill:#2c3e50,stroke:#fff,color:#fff
style C3 fill:#2c3e50,stroke:#fff,color:#fff
Lộ trình Shared Dictionaries trên Cloudflare — từ passthrough đến tự động hoàn toàn
Phase 1 — Bắt đầu từ 30/04/2026
Cloudflare sẽ forward các header Use-As-Dictionary, Available-Dictionary và các encoding dcb/dcz mà không strip hoặc recompress. Origin server tự quản lý dictionary — Cloudflare chỉ đảm bảo CDN layer không chặn protocol mới.
Bảo mật: Bài học từ SDCH và giải pháp RFC 9842
Năm 2008, Google triển khai SDCH (Shared Dictionary Compression over HTTP) trong Chrome — một ý tưởng tương tự. Tuy nhiên, SDCH bị loại bỏ vào 2017 do các lỗ hổng bảo mật nghiêm trọng:
- CRIME/BREACH attacks — kẻ tấn công có thể suy đoán nội dung encrypted response bằng cách quan sát kích thước nén thay đổi
- Vi phạm Same-Origin Policy — dictionary có thể được chia sẻ cross-origin, tạo kênh rò rỉ dữ liệu
RFC 9842 (chuẩn hóa tháng 9/2025) khắc phục triệt để:
| Vấn đề SDCH | Giải pháp RFC 9842 |
|---|---|
| Cross-origin dictionary sharing | Dictionary chỉ dùng được cho same-origin responses |
| Side-channel compression attacks | Dictionary hash là opaque — không tiết lộ nội dung |
| Privacy tracking qua dictionary | Dictionary tuân thủ cache partition (mỗi top-level site riêng biệt) |
| Không có chuẩn transport | HTTP header rõ ràng: Use-As-Dictionary, Available-Dictionary |
Chiến lược áp dụng thực tế cho dự án 2026
graph TD
START[Đánh giá dự án] --> Q1{Deploy > 1 lần/tuần?}
Q1 -->|Có| Q2{Bundle JS/CSS > 100KB?}
Q1 -->|Không| SKIP[Brotli/Zstd đủ tốt]
Q2 -->|Có| Q3{Target Chrome 130+?}
Q2 -->|Không| SKIP
Q3 -->|Có| IMPL[Triển khai Shared Dictionaries]
Q3 -->|Không| WAIT[Đợi Safari/Firefox hỗ trợ]
IMPL --> S1[Bước 1: Thêm Use-As-Dictionary header]
S1 --> S2[Bước 2: Xử lý Available-Dictionary request]
S2 --> S3[Bước 3: Serve dcb/dcz response]
S3 --> S4[Bước 4: Monitor compression ratio]
style START fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style IMPL fill:#e94560,stroke:#fff,color:#fff
style SKIP fill:#f8f9fa,stroke:#e0e0e0,color:#888
style WAIT fill:#f8f9fa,stroke:#ff9800,color:#2c3e50
style S1 fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
style S2 fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
style S3 fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
style S4 fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
Decision tree áp dụng Shared Dictionaries cho dự án web
Checklist triển khai
- Đo baseline — Kiểm tra kích thước bundle hiện tại với Gzip/Brotli. Nếu delta giữa 2 version liên tiếp nhỏ (dưới 5% thay đổi), Shared Dictionaries sẽ cực kỳ hiệu quả.
- Chọn thuật toán —
dcz(Zstandard) được khuyến nghị: nhanh hơn Brotli đáng kể khi dùng dictionary, hỗ trợ dictionary lớn hơn. - Cấu hình server — Thêm header
Use-As-Dictionarycho các static assets (JS, CSS, WASM). Xử lý headerAvailable-Dictionarytừ client. - CDN compatibility — Đảm bảo CDN (Cloudflare, CloudFront, Fastly) không strip các header mới. Mở rộng
Varyheader. - Fallback — Luôn serve Brotli/Gzip cho client không hỗ trợ. Progressive enhancement, không phải breaking change.
Lưu ý quan trọng
Shared Dictionaries là progressive enhancement. Client không hỗ trợ sẽ không gửi header Available-Dictionary và server fallback về Brotli/Gzip bình thường. Không có risk cho user cũ — chỉ có lợi cho user trên trình duyệt mới.
Tương lai: Từ Web đến Agentic Internet
Cloudflare nhấn mạnh một xu hướng quan trọng: trong kỷ nguyên Agentic Web, không chỉ người dùng mà cả AI agents cũng liên tục fetch trang web — và chúng request thường xuyên hơn con người rất nhiều. Shared Dictionaries giảm tải không chỉ cho end-user mà cho cả infrastructure khi lượng traffic từ agents tăng theo cấp số nhân.
Với RFC 9842 được chuẩn hóa, Chrome đã hỗ trợ, Firefox và Safari đang triển khai, cùng Cloudflare chuẩn bị beta — 2026 chính là năm Shared Dictionaries chuyển từ thí nghiệm sang production. Đây không phải tối ưu nhỏ lẻ — đây là thay đổi kiến trúc giúp web nhanh hơn một bậc cho mọi lần deploy tiếp theo.
Tham khảo
- Shared Dictionaries: compression that keeps up with the agentic web — Cloudflare Blog
- Supercharge compression efficiency with shared dictionaries — Chrome for Developers
- Dictionary Compression is finally here, and it's ridiculously good — HTTP Toolkit
- Compression Dictionary Transport — MDN Web Docs
- The Ultimate Guide to Shared Compression Dictionaries — DebugBear
- RFC 9842 — Compression Dictionary Transport
- Zstandard — Real-time data compression algorithm
Terraform vs OpenTofu 2026 — Chọn đúng công cụ Infrastructure as Code sau cuộc chia tách lịch sử
Cloudflare Workers — Xây dựng ứng dụng Full-Stack Serverless miễn phí trên Edge
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.