Mở tab Network của trình duyệt, bạn sẽ thấy một trang HTML "500 KB" thực ra chỉ truyền vài chục KB qua mạng. Đó là nén nội dung HTTP — có lẽ là tối ưu băng thông đơn giản mà hiệu quả nhất của web. Cơ chế đẹp ở chỗ nó trong suốt: client và server tự thương lượng, tự nén và giải nén, code ứng dụng không cần biết. Bài này đo thật tỉ lệ nén của ba thuật toán phổ biến — gzip, brotli, zstd — trên một trang HTML thật, và xem nginx thương lượng nén qua dây.
Thương lượng nén: hai header đối xứng
Cơ chế gói gọn trong hai header:
- Client → server:
Accept-Encoding: gzip, br, zstd— "tôi hiểu các thuật toán này". - Server → client:
Content-Encoding: gzip— "tôi đã nén bằng cái này".
Server nén body trước khi gửi, client tự giải nén khi nhận — ứng dụng nhận về nội dung gốc như chưa từng nén. Kèm theo thường có Vary: Accept-Encoding để cache biết bản nén khác nhau theo header này (không phục vụ nhầm bản gzip cho client chỉ hiểu raw).
gzip -9 -c f.html | wc -c # kích thước sau nén
brotli -q 11 -c f.html | wc -c
curl -H "Accept-Encoding: gzip" -D - URL # xem Content-Encoding
curl --compressed URL # tự khai Accept-Encoding + tự giải nén

Hình 1: Client mời (Accept-Encoding), server chọn (Content-Encoding) và nén trên đường truyền; client tự giải nén nên trong suốt. Vary: Accept-Encoding để cache phục vụ đúng bản.
Đo thật: tỉ lệ nén trên một trang HTML
Lấy một trang HTML thật (224.679 byte) và nén bằng ba công cụ ở mức cao nhất:

Hình 2: Trang HTML 224.679 byte nén còn: gzip -9 20.123 byte (91%), brotli 11 16.118 byte (93%, chặt nhất), zstd -19 18.151 byte (92%). nginx thương lượng: không Accept-Encoding trả raw 224679; có Accept-Encoding: gzip trả Content-Encoding: gzip. curl --compressed chỉ truyền 20.434 byte (~11 lần nhỏ hơn).
Kết quả rất thuyết phục:
- Tỉ lệ nén: cùng trang 224.679 byte, gzip -9 xuống 20.123 byte (còn 9%, tiết kiệm 91%), brotli mức 11 xuống 16.118 byte (còn 7.2%, tiết kiệm 93% — chặt nhất), zstd -19 xuống 18.151 byte (còn 8.1%). Với văn bản, cả ba đều tiết kiệm trên 90%.
- Thương lượng thật qua HTTP: gọi nginx (
gzip on) mà không khaiAccept-Encoding→ trả rawContent-Length: 224679; khaiAccept-Encoding: gzip→ trảContent-Encoding: gzip(kèmTransfer-Encoding: chunkedvì nginx nén dòng nên không biết trước độ dài). - Trong suốt với client:
curl --compressedtự khaiAccept-Encoding, nhận bản gzip (20.434 byte qua dây với mức nén mặc định của nginx) rồi tự bung về HTML gốc — người dùng không thấy gì khác ngoài trang tải nhanh hơn ~11 lần về băng thông.
brotli thắng về tỉ lệ, nhưng khác biệt so với gzip là vài phần trăm — đáng giá ở quy mô lớn, còn với đa số site thì gzip đã "đủ tốt" và phổ cập tuyệt đối.
Đánh đổi và lưu ý
Nén chỉ ăn với văn bản. HTML, CSS, JS, JSON, SVG nén rất tốt (nhiều lặp lại). Nhưng JPEG, PNG, MP4, file zip đã nén rồi — nén lại gần như không giảm mà còn tốn CPU (đôi khi tăng vài byte). Vì thế gzip_types của nginx chỉ liệt kê kiểu văn bản; đừng nén ảnh/video.
Mức nén là đánh đổi CPU đổi băng thông. brotli mức 11 hay gzip -9 nén chặt nhưng chậm — không hợp để nén động (on-the-fly) cho mỗi request. Thực tế: nén tĩnh sẵn ở mức cao nhất cho asset không đổi (pre-compress, phục vụ file .br/.gz có sẵn), và nén động ở mức trung bình (gzip 5–6) cho nội dung sinh động. nginx demo trên dùng mức 6 nên ra 20.434 byte, hơi lớn hơn gzip -9 (20.123) — đúng như kỳ vọng.
Cẩn thận nén + bí mật (BREACH/CRIME). Nén nội dung có chứa cả bí mật (token CSRF) và dữ liệu do kẻ tấn công điều khiển trên cùng response có thể rò rỉ bí mật qua kích thước nén. Đây là lý do một số framework tắt nén cho trang nhạy cảm hoặc tách bí mật ra. Hiếm nhưng có thật.
Ba ý mang về
- Nén HTTP thương lượng qua hai header đối xứng: client mời
Accept-Encoding, server chọnContent-Encodingvà nén trên dây, client tự giải nén — trong suốt;Vary: Accept-Encodingđể cache phục vụ đúng bản. - Tỉ lệ nén văn bản rất cao: đo thật trang HTML 224 KB xuống gzip 20 KB (91%), brotli 16 KB (93%, chặt nhất), zstd 18 KB (92%) — qua HTTP
curl --compressedtruyền ~11 lần ít hơn. - Biết giới hạn: chỉ nén văn bản (ảnh/video đã nén thì bỏ qua); mức nén cao đổi CPU lấy băng thông (pre-compress asset tĩnh, nén động mức vừa); và coi chừng rò rỉ bí mật qua nén (BREACH/CRIME) trên trang nhạy cảm.
Nguồn
- MDN — Content-Encoding và Accept-Encoding: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
- nginx docs — ngx_http_gzip_module: https://nginx.org/en/docs/http/ngx_http_gzip_module.html
Phần sau ta rẽ sang bảo mật đường truyền: TLS handshake từng bước — dùng openssl s_client xem ClientHello/ServerHello, chứng chỉ và phiên bản giao thức được thỏa thuận thế nào.