Optimization

Tối ưu Nginx cho traffic cao: worker, gzip, buffer, cache

Bạn vừa deploy một website lên VPS, mới đầu chạy ngon, nhưng khi traffic lên vài trăm người truy cập đồng thời, server bắt đầu chậm, CPU quay tít, thậm chí trả về 502 Gateway Timeout. Vấn đề thường gặp ở Nginx, cấu hình mặc định sinh ra để chạy ổn định chứ không phải để tận dụng tối đa tài nguyên VPS Linux bạn đang thuê. Bài viết này hướng dẫn bạn tuning 4 nhóm tham số cốt lõi: worker, gzip, buffer, và cache để Nginx xử lý hàng ngàn request đồng thời một cách mượt mà.

Yêu cầu trước khi bắt đầu

  • Một VPS chạy Ubuntu 24.04 LTS (các lệnh này tương thích với Debian 12 và AlmaLinux 9).
  • Nginx đã được cài đặt (dùng apt install nginx hoặc dnf install nginx).
  • Quyền sudo hoặc root.
  • Trang web hoặc ứng dụng đang chạy qua Nginx (để kiểm tra hiệu quả tối ưu).

Vì sao Nginx cần tuning?

File cấu hình mặc định của Nginx thiết kế để chạy trên bất kỳ máy nào, nên nó đặt worker_processes = 1 và buffer ở mức thấp. Trên một VPS NVMe giá rẻ với 2 CPU, điều này khiến bạn không thể tận dụng hết nhân CPU để xử lý request song song. Gzip bị tắt mặc định trong nhiều bản build, mỗi file HTML/CSS nặng đúng bằng gốc khi truyền tải. Buffer nhỏ khiến Nginx phải liên tục đọc/ghi disk I/O, gây tắc nghẽn. Cache thì không được bật sẵn. Kết quả: con số performance đo được chỉ bằng 1/3 khả năng thực sự.

Bước 1 - Tối ưu Worker Processes và Connections

Tham số worker_processes quyết định Nginx dùng bao nhiêu tiến trình để xử lý request. Với VPS dùng CPU Intel/AMD hiện đại (có hyper-threading), set bằng số lượng CPU core là tối ưu. Không nên set cao hơn: CPU sẽ bị oversubscribe, gây chậm.

Mở file cấu hình chính:

sudo nano /etc/nginx/nginx.conf

Tìm dòng worker_processes auto;, giá trị auto tự động phát hiện số core và set tương ứng. Nếu chưa có, thay thế hoặc thêm vào:

worker_processes auto;

Ngay dưới dòng đó, trong block events { }, set worker_connections cao hơn:

events {
    worker_connections 2048;
    multi_accept on;
    use epoll;
}

Giải thích: worker_connections = số kết nối tối đa mỗi worker xử lý. Tổng số kết nối tối đa = worker_processes × worker_connections. Với 2 nhân và 2048, bạn có 4096 kết nối đồng thời. multi_accept on cho phép một worker nhận nhiều kết nối mới trong một lần gọi. use epoll là cơ chế I/O event của Linux, hiệu quả hơn select/poll. Cả 3 dòng này giúp Nginx tận dụng tối đa CPU và xử lý hàng loạt request đồng thời.

Verify: sau khi thay đổi, kiểm tra cú pháp và reload:

sudo nginx -t
sudo systemctl reload nginx

Bước 2 - Kích hoạt và tinh chỉnh Gzip

Gzip nén file tĩnh (HTML, CSS, JS) trước khi gửi cho client, giảm kích thước tới 70%, giảm băng thông và tải server. Nhưng gzip tiêu tốn CPU, nếu site bạn nhiều request động, nên giảm mức nén (gzip_comp_level) hoặc tắt gzip cho API.

Thêm block sau vào file /etc/nginx/nginx.conf (trong block http { }):

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

Giải thích: gzip_comp_level 5 là mức cân bằng giữa tỉ lệ nén và CPU load (thang 1-9, 5 là điểm ngọt). gzip_min_length 256 chỉ nén file ≥256 bytes, tránh nén file rất nhỏ (tốn CPU vô ích). gzip_proxied any nén cả khi request qua proxy (cần cho reverse proxy). Dòng gzip_vary thêm header Vary: Accept-Encoding để CDN/proxy cache hoạt động đúng.

Verify: dùng curl kiểm tra header Content-Encoding: gzip:

curl -I -H "Accept-Encoding: gzip" http://yourip/

Bước 3 - Tinh chỉnh Buffer

Buffer giúp Nginx đọc nội dung response từ backend (PHP-FPM, Node.js) vào RAM trước khi gửi cho client. Buffer nhỏ khiến Nginx phải đọc/ghi file tạm lên disk liên tục, giết chết I/O trên ổ thường. Trên VPS NVMe có tốc độ cao, buffer lớn hơn vẫn có lợi vì giảm số lần system call.

Trong block http { }, thêm hoặc sửa:

client_body_buffer_size 16K;
client_header_buffer_size 1k;
large_client_header_buffers 2 1k;
client_max_body_size 50M;

proxy_buffer_size 8k;
proxy_buffering on;
proxy_buffers 32 16k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;

Giải thích: client_body_buffer_size = RAM dùng cho body request (POST). proxy_buffers 32 16k = tổng buffer cho response proxy là 32×16k = 512KB. Con số này đủ cho hầu hết trang web. Nếu site có nhiều file media lớn, có thể tăng lên 32×32k. proxy_busy_buffers_size = một phần buffer busy, set gấp đôi buffer size. proxy_temp_file_write_size = kích thước ghi file tạm khi buffer đầy, giá trị này nên bằng hoặc lớn hơn proxy_buffers.

Lưu ý: buffer dùng RAM, không set quá cao nếu VPS có RAM ≤ 1GB. Ví dụ VPS 1GB, tổng buffer proxy nên ≤ 256MB. Với VPS NVMe giá rẻ 2-4GB RAM, các giá trị trên hoàn toàn an toàn.

Bước 4 - Thiết lập Cache

Cache giảm tải cho backend và tăng tốc request lặp lại. Proxy cache của Nginx lưu file HTML đã render vào disk, lần sau request cùng URL trả thẳng từ Nginx, không qua PHP/Node.

Trong block http { }, khai báo đường dẫn cache:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mycache:10m max_size=1g inactive=60m use_temp_path=off;

Giải thích: levels=1:2 = cấu trúc thư mục cache 2 tầng (giúp disk I/O hiệu quả). keys_zone=mycache:10m = dùng tối đa 10MB RAM để lưu index key. max_size=1g = cache disk tối đa 1GB. inactive=60m = file cache bị xóa nếu không được dùng trong 60 phút. use_temp_path=off = tránh ghi file tạm ở thư mục khác (giảm I/O).

Sau đó, trong block server { } của site, bật cache cho proxy:

location / {
    proxy_pass http://localhost:8080;  # thay bằng backend thật
    proxy_cache mycache;
    proxy_cache_valid 200 302 60m;
    proxy_cache_valid 404 1m;
    add_header X-Cache-Status $upstream_cache_status;
}

Dòng add_header X-Cache-Status cho bạn biết request được serve từ cache (HIT) hay không (MISS), rất hữu ích để debug.

Tạo thư mục cache nếu chưa tồn tại và set quyền:

sudo mkdir -p /var/cache/nginx
sudo chown -R www-data:www-data /var/cache/nginx

Verify: truy cập site 2 lần, dùng curl check header X-Cache-Status: HIT:

curl -I http://yourip/ | grep -i x-cache
# Output kỳ vọng: X-Cache-Status: HIT

Xử lý lỗi thường gặp

  • Lỗi "too many open files": Nginx không mở được file. Giải pháp: tăng giới hạn của systemd sudo nano /etc/systemd/system/nginx.service.d/override.conf thêm LimitNOFILE=65536, sau đó sudo systemctl daemon-reload && sudo systemctl restart nginx.
  • Lỗi "ngx_slab_alloc() failed: no memory": keys_zone quá nhỏ. Tăng keys_zone=mycache:20m.
  • Gzip không hoạt động: Thiếu type. Kiểm tra dòng gzip_types đã liệt kê đúng MIME type (application/javascript không viết text/javascript chỉ có ở Firefox? Thực tế Nginx chấp nhận cả 2, nhưng nên viết đầy đủ).
  • Cache toàn bộ trang (kể cả admin): Thêm luật proxy_no_cache $cookie_sessionid; hoặc dùng proxy_cache_bypass $arg_nocache; để bypass cache cho request có cookie/param đặc biệt.

Bảng tham số nhanh

Tham sốGiá trị mặc địnhGiá trị tối ưu (VPS 2-4GB RAM)
worker_processes1auto
worker_connections5122048
gzip_comp_level1 (tắt)5
proxy_buffers8 4k32 16k
proxy_cache_path max_sizekhông có1g

Cấu hình worker_processes bao nhiêu là đủ?

Đặt auto là hàng đầu, Nginx tự động bằng số CPU core. Không nên hardcode con số cao hơn số core, vì CPU sẽ bị tranh chấp giữa các worker, giảm hiệu suất.

Gzip comp level 9 có tốt hơn level 5 không?

Level 9 nén nhiều hơn khoảng 5-10% so với level 5, nhưng tốn gấp 2-3 lần CPU. Với VPS có giới hạn CPU, level 5 là điểm cân bằng tối ưu.

Tôi có cần cache cho site WordPress không?

Có. Cache giảm tải cho PHP-FPM rõ rệt. Tuy nhiên, nên thêm rule bỏ qua cache cho trang admin (/wp-admin) và trang có cookie giỏ hàng.

Buffers chiếm bao nhiêu RAM?

Proxy_buffers 32×16k + busy_buffers 64k = khoảng 512KB mỗi worker. Với 2 worker, tổng chỉ 1MB, rất nhẹ. RAM chính nằm ở keys_zone (10MB) và cache disk (1GB).

Sao tôi thấy Nginx báo lỗi "no memory" sau khi set buffer lớn?

Bạn set proxy_buffers quá cao. Ví dụ 256×32k = 8MB mỗi worker, với nhiều worker song song, RAM nhanh chóng cạn. Giảm số lượng buffer xuống 32 hoặc 64.

Tối ưu này có áp dụng cho Nginx làm reverse proxy không?

Đúng. Toàn bộ các tham số worker, gzip, buffer, cache đều dùng được cho reverse proxy. Đặc biệt proxy_buffers và proxy_cache là hai nhóm quan trọng nhất cho reverse proxy.

Bài viết liên quan

Lưu ý: Bài viết mang tính tham khảo, tổng hợp kiến thức chung. Mỗi hệ thống, hạ tầng và nhu cầu có đặc thù riêng, nên kiểm thử trong môi trường an toàn và tham vấn kỹ sư trước khi triển khai thực tế.