Cấu hình Brotli và Gzip cho Nginx: nên dùng cách nào?

So sánh Brotli và Gzip trên Nginx, cách bật fallback, chọn MIME type, kiểm tra Content-Encoding và tránh lỗi khi module không có trên shared hosting.

NHT
· 9 phút đọc
Minh họa Nginx phân phối Brotli và Gzip qua hai luồng nén đến trình duyệt

Brotli và Gzip đều là các lựa chọn nén HTTP phổ biến khi bạn muốn giảm dung lượng HTML, CSS, JavaScript hoặc JSON truyền từ Nginx đến trình duyệt. Nhưng câu hỏi “nên dùng cách nào?” không có một đáp án cố định cho mọi máy chủ. Gzip thường dễ bật và có mặt trên nhiều bản Nginx, còn Brotli thường cho tỷ lệ nén tốt hơn với tài nguyên văn bản nhưng có thể cần module bổ sung.

Bài viết này tập trung vào cấu hình thực tế, cách kiểm tra response có thật sự được nén hay không và cách thiết kế fallback an toàn. Các ví dụ dùng Nginx trên Linux; hãy thay đường dẫn và MIME type theo hệ thống của bạn. Nếu đang chạy shared hosting, trước tiên hãy hỏi nhà cung cấp module nào được bật, vì bạn có thể không có quyền cài dynamic module hoặc sửa cấu hình ở context http.

Câu trả lời ngắn: hãy bật Gzip như lớp tương thích cơ bản, dùng Brotli khi bản Nginx và module của bạn hỗ trợ ổn định, sau đó kiểm tra bằng curl -H "Accept-Encoding: br"curl -H "Accept-Encoding: gzip". Không nén mù mọi MIME type, không nén lại file đã nén và luôn kiểm tra header Vary: Accept-Encoding.

Brotli và Gzip khác nhau ở điểm nào?

Gzip sử dụng định dạng dựa trên DEFLATE và được Nginx hỗ trợ qua module ngx_http_gzip_module. Tài liệu Nginx mô tả module này là một filter nén response bằng phương pháp gzip; directive có thể đặt ở mức http, server hoặc location [1]. Brotli là thuật toán lossless hiện đại hơn, dựa trên biến thể LZ77, Huffman coding và second-order context modeling. NGINX Plus phân tách module Brotli thành filter module để nén on-the-fly và static module để phục vụ file đã nén sẵn [2].

Trong thực tế, tỷ lệ nén không phải tiêu chí duy nhất. Brotli ở mức chất lượng cao có thể tốn CPU hơn; Gzip có hệ sinh thái lâu đời, dễ chẩn đoán và thường có sẵn hơn. Trình duyệt sẽ gửi Accept-Encoding để báo định dạng nó hiểu, còn server chọn một content encoding phù hợp. Vì thế bạn không cần ép mọi client dùng Brotli; fallback đúng mới là mục tiêu.

Khi nào nên chọn Gzip?

Gzip là lựa chọn hợp lý khi bạn cần một cấu hình portable cho nhiều distro, container hoặc shared hosting. Nó phù hợp với HTML, CSS, JavaScript, XML, JSON, SVG và các response văn bản đủ lớn để chi phí CPU có ý nghĩa. Nginx mặc định chỉ nén text/html; các MIME type khác phải thêm bằng gzip_types [1].

Đừng bật gzip_types * một cách máy móc. Ảnh JPEG, PNG, WebP, video MP4, ZIP, WOFF2 và nhiều định dạng đã nén gần như không có lợi khi nén lại, thậm chí làm tăng CPU và độ trễ. Hãy bắt đầu từ danh sách MIME type thực tế trong ứng dụng, sau đó đo kích thước trước và sau.

http {
    gzip on;
    gzip_comp_level 5;
    gzip_min_length 1000;
    gzip_vary on;
    gzip_proxied any;
    gzip_types
        text/plain
        text/css
        text/xml
        application/xml
        application/json
        application/javascript
        application/rss+xml
        image/svg+xml;
}

Mức gzip_comp_level nằm trong khoảng 1 đến 9 theo tài liệu Nginx [1]. Không nên mặc định chọn 9: mức cao hơn thường tăng CPU và thời gian nén, trong khi lợi ích dung lượng có thể nhỏ. Mức 4–6 là điểm bắt đầu thực dụng cho nhiều website, nhưng quyết định cuối cùng nên dựa trên benchmark response thật.

Khi nào nên chọn Brotli?

Brotli đáng cân nhắc khi website phục vụ nhiều tài nguyên văn bản và bạn kiểm soát được bản Nginx, module cũng như quy trình reload. Theo tài liệu NGINX, module Brotli có thể được cài từ repository NGINX Plus, load bằng load_module ở main context rồi bật bằng brotli on; trong http, server hoặc location [2]. Với Nginx OSS, cách cài phụ thuộc distro, package third-party hoặc quy trình build; không được giả định rằng directive này tồn tại.

Nếu bạn chỉ có quyền chỉnh một file virtual host trên shared hosting, hãy kiểm tra bằng nginx -t hoặc hỏi host trước khi thêm directive Brotli. Một cấu hình có brotli on; nhưng binary không có module sẽ làm Nginx từ chối reload. Trong môi trường không kiểm soát được module, Gzip ổn định vẫn tốt hơn một cấu hình Brotli không thể vận hành.

# Chỉ dùng khi binary đã có ngx_brotli
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

http {
    brotli on;
    brotli_comp_level 5;
    brotli_static on;
    brotli_types
        text/plain
        text/css
        application/javascript
        application/json
        image/svg+xml;
}

Hai module static và filter có vai trò khác nhau: filter nén response khi request đến, còn static phục vụ file được tạo sẵn với đuôi phù hợp. Nếu pipeline build đã tạo .br, static serving có thể giảm CPU runtime; nếu chưa có pipeline, filter on-the-fly dễ triển khai hơn nhưng cần theo dõi tải.

Có nên bật cả Brotli và Gzip không?

Thông thường, có thể bật cả hai để tạo fallback theo khả năng client. Client hỗ trợ Brotli sẽ gửi br trong Accept-Encoding; client chỉ hỗ trợ Gzip sẽ nhận gzip; client không hỗ trợ hai định dạng sẽ nhận response nguyên bản. Quyết định ưu tiên encoding do Nginx/module và header request thực tế xử lý, không nên tạo hai bản response bằng cách tự viết logic redirect.

Điểm cần nhớ là cache phải phân biệt response theo Accept-Encoding. Với Gzip, directive gzip_vary on; thêm header Vary: Accept-Encoding [1]. Khi có CDN hoặc reverse proxy ở phía trước, header này giúp cache không trả nhầm bản Brotli cho client chỉ hiểu Gzip. Sau khi bật Brotli, hãy xác nhận module của bạn cũng tạo hành vi Vary tương đương và kiểm tra CDN.

server {
    listen 443 ssl http2;
    server_name example.com;

    gzip on;
    gzip_vary on;
    gzip_min_length 1000;
    gzip_types text/plain text/css application/javascript application/json image/svg+xml;

    # Chỉ bật nếu module Brotli thực sự tồn tại
    brotli on;
    brotli_comp_level 5;
    brotli_types text/plain text/css application/javascript application/json image/svg+xml;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

Nếu package của bạn không hỗ trợ directive Brotli ở context đang chỉnh sửa, hãy bỏ phần Brotli và giữ cấu hình Gzip đã kiểm thử. Không copy nguyên block từ một máy chủ khác mà không kiểm tra nginx -V, đường dẫn module và tài liệu của distro.

Cấu hình mức nén, MIME type và response nhỏ

gzip_min_length giúp tránh tốn CPU cho response quá nhỏ. Một response vài trăm byte có thể nhỏ đến mức header và chi phí nén làm lợi ích không đáng kể. gzip_types chỉ nên chứa loại dữ liệu văn bản thật sự. Với JSON API, hãy thêm application/json; với frontend hiện đại, thường cần text/css, application/javascript và có thể image/svg+xml.

Không nên dùng gzip_comp_level như một chỉ số SEO độc lập. Compression thường giúp giảm byte truyền và cải thiện trải nghiệm, nhưng tốc độ cuối cùng còn phụ thuộc TTFB, cache, HTTP/2 hoặc HTTP/3, kích thước JavaScript, ảnh và thiết bị người dùng. Hãy đo Lighthouse hoặc WebPageTest trước và sau thay đổi, đồng thời quan sát CPU.

Bảo mật: lưu ý BREACH khi nén HTTPS

Tài liệu module Gzip của Nginx cảnh báo response nén qua SSL/TLS có thể chịu ảnh hưởng của tấn công BREACH [1]. Đây không phải lý do để tắt nén mọi website, nhưng là lý do để đánh giá các response có chứa bí mật phản chiếu trong nội dung và có thể bị đoán qua kích thước. Với form nhạy cảm, token CSRF hoặc trang có dữ liệu bí mật phản chiếu, cần tham khảo hướng dẫn bảo mật của framework và cân nhắc tắt nén ở location phù hợp.

location /sensitive-form {
    gzip off;
    # Chỉ là lớp giảm rủi ro; vẫn cần xử lý CSRF và thiết kế response đúng
    proxy_no_cache 1;
}

Block trên không phải bản vá BREACH hoàn chỉnh. Không nên thêm nó mà bỏ qua CSRF, cookie flags, cache policy và kiểm soát dữ liệu phản chiếu. Hãy coi đây là ví dụ về việc tách policy nén cho từng location khi threat model yêu cầu.

Kiểm tra Nginx có hỗ trợ gì trước khi reload

Trước khi chỉnh file, kiểm tra binary và module. Một số hệ thống dùng package module động, một số build module tĩnh, còn shared hosting có thể khóa toàn bộ cấu hình. Chụp lại cấu hình hiện tại hoặc commit file nếu bạn quản lý bằng Git để rollback được.

nginx -V 2>&1 | tr -- ' ' '\n' | grep -E 'brotli|gzip|modules'
nginx -t
sudo systemctl reload nginx

# Kiểm tra response Brotli
curl -sSI -H "Accept-Encoding: br" https://example.com/app.js

# Kiểm tra response Gzip
curl -sSI -H "Accept-Encoding: gzip" https://example.com/app.js

# Kiểm tra không nén khi client không quảng cáo encoding
curl -sSI -H "Accept-Encoding: identity" https://example.com/app.js

Trong output, chú ý Content-Encoding: br hoặc Content-Encoding: gzip, Vary: Accept-Encoding, Content-Type, Content-Length hoặc chuyển sang chunked response. Nếu curl không thấy encoding, có thể file nhỏ hơn ngưỡng, MIME type chưa nằm trong danh sách, upstream đã nén sẵn hoặc request đang đi qua CDN có policy riêng.

Checklist triển khai an toàn

  • Xác định bạn đang dùng Nginx OSS, NGINX Plus hay package do hosting cung cấp; không dùng directive Brotli nếu module chưa tồn tại.
  • Bật Gzip trước nếu cần fallback phổ biến, đặt MIME type có chủ đích và thử mức nén vừa phải.
  • Chỉ bật Brotli sau khi kiểm tra module, context được phép và đường dẫn dynamic module.
  • Giữ Vary: Accept-Encoding để CDN/cache phân biệt các bản response.
  • Không nén lại ảnh, video, archive hoặc font đã nén nếu benchmark không chứng minh có lợi.
  • Chạy nginx -t trước reload và giữ phương án rollback nếu reload lỗi.
  • Đo header, dung lượng, CPU và Core Web Vitals thay vì chỉ nhìn một điểm Lighthouse.

Kết luận: nên dùng Brotli hay Gzip?

Nếu cần câu trả lời thực dụng, hãy bắt đầu với Gzip vì dễ tương thích và dễ chẩn đoán. Khi có quyền kiểm soát module và thấy website phục vụ nhiều tài nguyên text, thêm Brotli để client hỗ trợ nhận bản nén phù hợp. Hai công nghệ không loại trừ nhau; kiến trúc tốt là có fallback, cache đúng Vary, MIME type hợp lý và quy trình kiểm tra trước reload.

Bạn có thể kết hợp bài này với hướng dẫn cấu hình static file và cache trình duyệt trong Nginx để xử lý cả compression lẫn cache policy. Khi chạy nhiều website trên cùng một máy, hãy kiểm tra context của từng server block theo hướng dẫn Nginx reverse proxy nhiều website.

Nguồn tham khảo

[1] NGINX ngx_http_gzip_module; [2] NGINX Brotli dynamic module; [3] MDN Content-Encoding; [4] google/ngx_brotli.