Phase tối ưu tốc độ load toàn site (2026-10)

Ngày: 2026-10-18 · Trạng thái: PLAN — chờ user chốt mục 6 Tiền đề: PHASE-PERF-OPTIMIZATION.md (2026-06) — phần hạ tầng đã làm (kiểm tra lại mục 1.3). Plan này nhắm phần còn lại: trình duyệt + Cloudflare.


1. Đo thực tế (2026-10-18, prod)

1.1 Server rất nhanh — nút thắt không nằm ở backend

Đo Trên VPS (127.0.0.1) Từ ngoài (qua Cloudflare + NPM)
HTML trang chủ / sản phẩm 35–78 ms 300–800 ms
API danh mục / sản phẩm / mẫu 18–53 ms 240–420 ms

1.2 Trình duyệt phải tải (mobile 390px, cache trống)

Trang Dung lượng Request LCP (mạng thật) Ghi chú
/ 4,4 MB (ảnh 3,9 MB) 165 1,9 s 26/26 ảnh là ảnh gốc 1.800–3.600px, hiển thị 110–360px
/san-pham/[slug] 2,8 MB (font mẫu 1,6 MB) 221 2,1 s 1 font mẫu 491 KB; JS route 366 KB
/san-pham 2,1 MB 142 0,4 s ảnh thumbnail mẫu 678 KB
/gio-hang, /dang-nhap ~0,45 MB 89–122 ~0,3 s 18 file font, 38 chunk JS

1.3 Lighthouse mobile (4G giả lập)

Trang Điểm FCP LCP TBT CLS
/ 61 3,8 s 15,3 s 180 ms 0
/san-pham/thiep-cuoi-1 17 3,1 s 11,0 s 3.270 ms 0,398
/dang-nhap 52 3,6 s 7,3 s 390 ms 0,156

1.4 Hạ tầng đã làm (plan 2026-06) — không cần làm lại

Postgres shared_buffers 2,5 GB / work_mem 16 MB / random_page_cost 1.1 ✅ · Redis maxmemory 512 MB + LRU ✅ · uvicorn 4 worker, celery 4 ✅ · NPM HTTP/2 + /_next/static cache 1 năm ✅ · nén Brotli (Cloudflare) ✅.


2. Nguyên nhân gốc (xếp theo tác động)

# Vấn đề Bằng chứng Ảnh hưởng
A Ảnh gốc không qua tối ưu — mọi ảnh CMS / sản phẩm / mẫu là <img> thô trỏ /api/v1/cms-anh/*/file (JPEG gốc tới 2,7 MB) 0/26 ảnh trang chủ qua /_next/image; cùng ảnh qua /_next/image?w=640 = 45 KB AVIF (nhỏ 60×) LCP trang chủ 15 s, 3,9 MB ảnh
B Mọi khách mới tải trang 2 lần — CacheBuster (dọn SW 1 lần, 2026-06) thấy localStorage trống ⇒ location.reload() với mọi khách mới 2 request document cho 1 lượt mở Lần đầu vào chậm gấp đôi
C Cloudflare không cache gì (cf-cache-status: DYNAMIC cho JS, font, ảnh) và ghi đè cache 1 năm của /_next/static thành 16 giờ header cache-control: max-age=57597 dù origin gửi immutable Mọi file tĩnh đi về tận VPS; khách quay lại vẫn tải lại sau 16 h
D /api/auth/session gọi 14 lần / trang — apiClient gọi getSession() trước mỗi request, không cache 14 fetch session trên /dang-nhap Thêm ~14 round-trip, chiếm kết nối
E Trang sản phẩm nặng JS — preview tương tác (Konva) + tải toàn bộ font của mẫu (491 KB/font) ngay khi mở TBT 3,3 s, JS exec 6,5 s, 1,6 MB font Điểm 17, máy yếu đơ vài giây
F Xô lệch bố cục (CLS) — khung preview / ảnh không giữ chỗ trước khi tải CLS 0,398 (sản phẩm), 0,156 (đăng nhập) Nội dung nhảy khi đang đọc / bấm
G 18 file font giao diện (4 độ đậm × 3 bộ ký tự), không preload 117–193 KB font mỗi trang FCP chậm hơn

3. Kế hoạch

Giai đoạn 1 — Sửa nhanh, tác động lớn (~1 ngày, chỉ frontend)

Việc Cách làm Ước tính
B Bỏ tải lại trang với khách mới CacheBuster: chỉ reload khi thực sự có service worker cũ / cache cũ; khách mới chỉ ghi khoá phiên bản 15 phút
D Cache session ở apiClient Ghi nhớ kết quả getSession() ~30 s (dùng chung 1 promise khi nhiều request song song); xoá khi đăng nhập/đăng xuất/401 45 phút + test
A Ảnh qua /_next/image Helper anhToiUu(url, rong) dùng lại bộ tối ưu sẵn có (AVIF/WebP, đã cấu hình images trong next.config.ts); áp cho: thẻ danh mục, quạt ảnh hero, SanPhamCard, CarouselStrip, FeatureColumns, PromoBlocks, BannerNho, thumbnail mẫu (file-asset), giỏ hàng, gallery sản phẩm. Thêm loading="lazy" + sizes; ảnh LCP đầu trang fetchpriority="high" 3–4 giờ
F Giữ chỗ chống xô lệch aspect-ratio cố định cho khung preview sản phẩm, ảnh gallery, logo; kiểm tra lại CLS đăng nhập (nghi popup/thông báo chèn vào) 1–2 giờ

Kỳ vọng: trang chủ 4,4 MB → ~0,7 MB, LCP lab 15 s → ~3 s; khách mới không còn tải 2 lần; bớt ~13 request/trang.

Giai đoạn 2 — Cloudflare (cần bạn: quyền dashboard hoặc API token)

Việc Cài đặt
C1 Tôn trọng header cache của origin Caching → Configuration → Browser Cache TTL = "Respect Existing Headers"
C2 Cache ở edge (gần khách VN) Cache Rules: /_next/static/*, /_next/image*, /brand/*, /img/*, font → Eligible for cache, Edge TTL theo header origin; /api/v1/cms-anh/*/file → cache 1 ngày
C3 Không cache HTML / API động Giữ mặc định (đang no-store)

Kỳ vọng: file tĩnh trả từ edge Cloudflare (thường < 50 ms ở VN) thay vì về VPS; khách quay lại không tải lại JS/font.

Đổi cấu hình dịch vụ ngoài → mình không tự làm. Bạn cấp API token Cloudflare (quyền Zone → Cache Rules + Zone Settings, chỉ zone insacmauviet.vn) hoặc mình gửi hướng dẫn từng bước để bạn bấm.

Giai đoạn 3 — Trang sản phẩm nhẹ lại (~1 ngày)

Việc Cách làm
E1 Hiện ảnh preview tĩnh trước Lần đầu mở: ảnh render sẵn của mẫu (đã có hinh_anh_url / thumbnail) qua /_next/image; chỉ tải Konva + preview tương tác khi khách bấm "Tuỳ chỉnh" hoặc sau khi trang rảnh (requestIdleCallback)
E2 Font mẫu tải theo nhu cầu Chỉ tải font của trường đang hiển thị / đang sửa; dùng bản subset web (backend đã có cờ subset — migration 0071) thay font đủ; font-display: swap
E3 Tách bundle dynamic(() => import(...), { ssr: false }) cho InteractiveTemplatePreview, BasicEditPanel, MockupPreview; kiểm tra lại JS route (366 KB → mục tiêu < 200 KB)

Kỳ vọng: TBT 3,3 s → < 600 ms, điểm 17 → 60+.

Giai đoạn 4 — Font giao diện + dọn nhỏ (~2 giờ)

Giai đoạn 5 — Ảnh gốc nhẹ từ đầu (tuỳ chọn, backend + dữ liệu)


4. Đo lại & chống thoái lui

5. Thứ tự & ước lượng

Giai đoạn Thời gian Phụ thuộc Deploy
1 Sửa nhanh (B, D, A, F) ~1 ngày — riêng
2 Cloudflare 30 phút quyền Cloudflare từ bạn —
3 Trang sản phẩm ~1 ngày GĐ1 riêng
4 Font + dọn nhỏ ~2 giờ — gộp GĐ3
5 Nén ảnh gốc (tuỳ chọn) ~0,5 ngày xác nhận của bạn riêng

Không đổi DB schema, không thêm thư viện.

6. Cần bạn chốt