Tối ưu server: đo trước, sửa sau — và những con số thật từ một VPS 3GB
Chúng tôi đo toàn bộ đường đi của một yêu cầu, từ trình duyệt tới cơ sở dữ liệu. Máy chủ trả lời trong 3 mili giây, người dùng chờ 130. Bài này chỉ ra cách tách thời gian xử lý, đường truyền và tải tài nguyên.

Câu hỏi "làm sao cho web nhanh hơn" gần như luôn được trả lời sai, vì nó được trả lời trước khi đo. Người ta nâng CPU, thêm RAM, đổi sang máy chủ mạnh hơn — rồi phát hiện trang vẫn chậm y như cũ.
Bài này kể lại đúng những gì chúng tôi đo được trên hệ thống của mình, theo đúng thứ tự đã đo, kèm con số.
Hệ thống được đo
Bản thảo gốc ghi các số liệu dưới đây vào ngày 18/10/2026. Chưa có log gốc trong thư mục để xác minh độc lập. Đây là quan sát ở một cấu hình, không phải benchmark đại diện; cần đo lại dưới tải thực tế.
Một VPS: 4 nhân, 2.9 GB RAM, đặt tại Việt Nam
Trên đó: trang web công khai dựng phía máy chủ (Node), màn hình quản trị (tệp tĩnh), API (Go), PostgreSQL 16
Và một ứng dụng của một công ty khác chạy chung máy
Bước 1: đo từ trong ra ngoài, không đo từ ngoài vào
Sai lầm thường gặp là mở trang web, thấy chậm, rồi kết luận "server yếu". Phép đo đó gộp chung bốn thứ: máy chủ, đường truyền, CDN, và cân nặng trang. Phải tách ra.
Đo ngay trên máy chủ, qua loopback — loại bỏ hoàn toàn mạng:
SSR nội bộ: 3.1 ms 3.3 ms 2.9 ms
API nội bộ /members: 5.1 ms
API nội bộ /menus: 4.3 ms
API nội bộ /settings: 3.7 ms
Tải trung bình: 0.06
RAM còn trống: 1.9 GBỞ các mẫu này, chưa thấy dấu hiệu máy chủ là nút thắt. Load average 0.06 không có nghĩa CPU rảnh 99%. Cần thêm CPU, I/O, bộ nhớ, số yêu cầu đồng thời và p95/p99 dưới tải trước khi kết luận về năng lực máy.
Nếu bạn chỉ làm một việc trong bài này, hãy làm phép đo trên. Nó mất ba phút và nó quyết định toàn bộ phần còn lại.
Bước 2: đo từ ngoài, cùng một thứ
Từ máy người dùng (Việt Nam), nối thẳng:
bắt tay TLS: 0.06–0.08 s
TTFB: 0.08–0.10 s
tổng: 0.10–0.13 sKhông nên lấy tổng thời gian tải bên ngoài trừ thời gian phản hồi nội bộ rồi gọi phần dư là độ trễ mạng. Hãy đo cùng URL, cùng nội dung và tách DNS, TCP, TLS, TTFB, tải body; đo nhiều lần với cache và kết nối được kiểm soát.
Bước 3: cân nặng trang — nơi tiền thật sự nằm
Đây là chỗ chúng tôi tìm ra vấn đề lớn nhất, và nó không liên quan gì tới máy chủ:
Tài sản | Gốc | Sau nén |
|---|---|---|
| 482 KB | 152 KB |
| 318 KB | — |
CSS | 135 KB | 30 KB |
Ảnh đại diện một người | 697 KB | 516 KB (WebP) |
| 140 KB | — |
| 49 KB | — |
18 tệp phông chữ | 290 KB | — |
Ảnh chân dung này nặng hơn tệp entry-client.js sau nén. Chưa đủ dữ liệu để kết luận về tổng JavaScript của trang. 516 KB cho một khung hiển thị rộng khoảng 320 điểm ảnh.
Tệ hơn: khi chúng tôi thử yêu cầu bản nhỏ bằng tham số ?w=320, máy chủ bỏ qua tham số và vẫn trả ảnh gốc. Nghĩa là có sẵn cơ chế thay đổi kích thước nhưng nó không hoạt động — loại lỗi chỉ lộ ra khi đo, vì trang vẫn hiển thị đúng.
Bài học
Thứ tự tối ưu theo mức hiệu quả trên công sức, với một trang web điển hình:
Ảnh — gần như luôn là thứ nặng nhất, và gần như luôn dễ sửa nhất
Nén — kiểm tra xem máy chủ có thật sự nén không, đừng tin cấu hình
Cache ở biên cho tệp tĩnh
Mã JavaScript — khó và lâu nhất, để sau cùng
Phần cứng — khi số đo cho thấy CPU, RAM hoặc I/O là giới hạn
Bước 4: kiểm tra nén có thật sự chạy không
Cấu hình gzip on; trong nginx không có nghĩa là nó đang nén thứ bạn nghĩ. text/html luôn được module gzip hỗ trợ; muốn nén CSS và JavaScript, cần khai báo MIME type phù hợp trong gzip_types.
Đừng đọc file cấu hình rồi kết luận. Hãy đo.
Cách kiểm tra đúng là hỏi như một trình duyệt thật và nhìn số byte tải về:
curl -s -o /dev/null --compressed -w '%{size_download}' https://ten-mien/Đối chiếu thêm header Content-Encoding và cùng một response không nén. Số byte nhỏ hơn chưa tự chứng minh thuật toán nén nào được dùng. Bản thảo ghi HTML khoảng 91 KB và body truyền về khoảng 21 KB; chưa có header gốc để xác nhận zstd.
Bước 5: cẩn thận với phép đo tự lừa mình
Đây là sai lầm tôi đã mắc trong chính lần đo này, và nó đáng kể lại.
Lần đầu tôi đo tốc độ qua CDN bằng cách chạy curl từ chính máy chủ. Kết quả: 348–838 mili giây. Tôi suýt kết luận CDN làm chậm gấp tám lần.
Nhưng phép đo đó sai về bản chất: yêu cầu đi từ máy chủ → ra Internet → tới CDN → quay ngược lại chính máy chủ đó. Nó đo một đường vòng mà không người dùng nào đi.
Đo lại từ một máy người dùng thật ở Việt Nam, bản thảo ghi 0.31–0.49 giây. Tuy nhiên hai đường khi đó đo hai ứng dụng khác nhau; chưa thể dùng chênh lệch này để định lượng chi phí của CDN.
Nguyên tắc: đo ở đúng vị trí người dùng đứng. Một phép đo tiện tay thường là một phép đo sai.
Bước 6: những thứ không nằm trong tầm kiểm soát
Có những nút thắt bạn đo ra nhưng không sửa được, và biết sớm thì đỡ mất thời gian. Chúng tôi gặp một cái rất lạ: kết nối cổng 80 từ một số mạng quốc tế không tới được máy.
Triệu chứng là chứng chỉ SSL không cấp được, báo "connection reset". Mọi dấu hiệu đều chỉ về phía cấu hình máy chủ. Nhưng bắt gói tin cho thấy sự thật:
Từ mạng nước ngoài gọi vào cổng 80: KHÔNG một gói SYN nào tới máy
Từ mạng trong nước gọi vào cổng 80: SYN tới, máy trả SYN-ACKGói tin bị chặn trước khi tới máy. Không cấu hình nào trên máy sửa được điều đó.
Bài học ở đây không phải về cổng 80. Là về phép đối chứng: nếu tôi chỉ chạy phép đo từ nước ngoài và thấy không có gói nào, tôi có thể kết luận bộ lọc gói tin của mình hỏng. Chính phép đo đối chứng từ trong nước — thấy gói tin tới bình thường — giúp khoanh vùng vấn đề có thể nằm ở đường đi hoặc bộ lọc phía trước máy. Chưa đủ bằng chứng để khẳng định nhà cung cấp chủ động chặn cổng.
Mọi phép đo cho kết quả "không có gì" đều cần một phép đối chứng cho kết quả "có". Nếu không, bạn không phân biệt được "không có gì xảy ra" với "dụng cụ đo hỏng".
Danh sách kiểm tra
Chạy theo thứ tự này, dừng lại ngay khi tìm ra nút thắt:
Đo trên loopback — máy chủ có chậm không? (thường là không)
Đo từ máy người dùng thật — chênh lệch nằm ở đâu?
Liệt kê tài sản tĩnh theo kích thước giảm dần — ảnh có nằm đầu không?
Kiểm tra nén bằng
--compressed, nhìn số byteKiểm tra cache ở biên: header
cf-cache-statushoặc tương đươngChỉ khi cả năm bước trên đều sạch, mới nghĩ tới phần cứng
Số liệu được giữ từ bản thảo về Zukatek, tháng 10/2026; chưa xác minh độc lập trong lần rà soát này. Con số của bạn sẽ khác. Hãy giữ phương pháp và lưu log của từng lần đo.
Nguồn đối chiếu: nginx gzip module.


