Khi trình duyệt gặp một thẻ <link rel="stylesheet"> trong <head>, nó dừng việc vẽ trang lại cho tới khi tệp CSS đó được tải về, phân tích và xử lý xong. CSS vì thế là tài nguyên chặn hiển thị (render-blocking — chặn việc vẽ trang). Trên mạng chậm hoặc máy yếu, vài trăm mili-giây chờ một tệp CSS nặng đủ để người dùng thấy màn hình trắng lâu hơn cần thiết.
Ý tưởng của kỹ thuật này là tách đôi: phần CSS thật sự cần cho khung hình đầu tiên thì đưa thẳng vào HTML, phần còn lại để sau. Như vậy trình duyệt có ngay đủ style để vẽ, không phải chờ tải hết tệp.
Vì sao tách CSS tới hạn giúp FCP và LCP
Hai chỉ số chịu ảnh hưởng trực tiếp là FCP (First Contentful Paint — thời điểm nội dung đầu tiên hiện ra) và LCP (Largest Contentful Paint — thời điểm phần tử lớn nhất hiện ra). Cả hai phụ thuộc vào việc trình duyệt sớm hoàn tất “đường tới hạn của render” (critical rendering path — chuỗi bước bắt buộc để vẽ khung hình đầu).
Một tệp CSS chung thường gói toàn bộ style của cả site: header, footer, trang sản phẩm, popup, trang liên hệ… Nhưng để vẽ khung hình đầu tiên, trình duyệt chỉ cần một phần rất nhỏ trong số đó. Khi bạn inline đúng phần nhỏ này và bỏ thuộc tính chặn hiển thị khỏi tệp lớn, trình duyệt không còn phải chờ tải tệp CSS bên ngoài trước khi vẽ. Kết quả là FCP đến sớm hơn, và nếu phần tử LCP nằm trong vùng above-the-fold thì LCP cũng cải thiện theo.
Theo ngưỡng “Good” hiện hành của Core Web Vitals (đo ở phân vị 75 — p75 của người dùng thật): LCP ≤ 2,5 giây, INP ≤ 200 ms, CLS ≤ 0,1. INP (Interaction to Next Paint — độ trễ tới khung hình kế sau tương tác) đã chính thức thay FID làm chỉ số Core Web Vital từ tháng 3/2024. Critical CSS chủ yếu tác động lên nhánh LCP/FCP — phần “vẽ nhanh”, không phải phần phản hồi tương tác.
Muốn hiểu rõ vì sao FCP và LCP là hai mốc khác nhau, xem bài phân biệt FCP và LCP.
Quy trình ba bước
- Trích (extract): tìm đúng các quy tắc CSS chi phối nội dung above-the-fold ở từng kích thước màn hình.
- Inline: nhúng phần CSS đó vào trong một thẻ
<style>đặt ngay trong<head>. - Tải hoãn (defer): nạp phần CSS còn lại theo cách không chặn hiển thị.
Kỹ thuật phổ biến cho bước ba là dùng thuộc tính media kết hợp onload:
<head>
<style>
/* Critical CSS đã trích, đủ vẽ khung hình đầu */
header{...} .hero{...} h1{...}
</style>
<link rel="stylesheet" href="/style.css"
media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/style.css"></noscript>
</head>Mẹo ở đây: gán media="print" khiến trình duyệt coi tệp như chỉ dùng khi in, nên không chặn hiển thị; khi tải xong, onload đổi lại thành all để áp cho màn hình. Thẻ <noscript> là phương án dự phòng cho trình duyệt tắt JavaScript.

Công cụ để trích Critical CSS
Trích tay rất dễ sai và khó bảo trì, nên thực tế người ta dùng công cụ tự động hoá trong bước build hoặc CI (continuous integration — tích hợp liên tục):
| Công cụ | Cơ chế | Hợp với |
|---|---|---|
critical (Addy Osmani) | Dùng trình duyệt ẩn (headless) đo thực tế vùng above-the-fold | Trang tĩnh, Gulp/CLI |
penthouse | Headless, truyền CSS gốc + URL để trích | Pipeline Node.js tuỳ biến |
| Beasties | Phân tích HTML đã render, KHÔNG cần headless nên nhanh và nhẹ | Vite/Webpack, framework |
Lưu ý cập nhật: Critters (plugin từng rất phổ biến của GoogleChromeLabs) nay đã ngừng phát triển và lưu trữ (archive). Bản kế nhiệm được duy trì là Beasties (fork do Daniel Roe/Nuxt giữ). Nếu dự án bạn còn dùng Critters thì nên chuyển sang Beasties.
Trên WordPress, nhiều plugin cache (như LiteSpeed Cache) có sẵn tính năng sinh và inline critical CSS theo từng loại trang — đây là cách thực dụng nhất cho người không build bằng tay. Web22 vận hành chính web22.dev trên nền LiteSpeed và thấy việc bật critical CSS theo mẫu trang giúp giảm thời gian màn hình trắng rõ rệt trên di động, dù mức cải thiện cụ thể luôn phụ thuộc theme và lượng CSS gốc.

Nhược điểm cần biết trước khi áp dụng
- Dễ lỗi thời (drift): mỗi lần đổi giao diện, critical CSS cũ có thể thiếu quy tắc mới, gây nhấp nháy bố cục (FOUC — flash of unstyled content). Vì vậy phải tự động sinh lại trong CI, đừng inline thủ công một lần rồi quên.
- Khác nhau theo viewport: above-the-fold trên điện thoại và trên màn hình rộng không giống nhau. Trích theo một kích thước rồi áp cho tất cả có thể thiếu style.
- Giới hạn dung lượng: phần inline làm HTML phình ra; nên giữ critical CSS gọn (thông lệ khuyên dưới khoảng 14 KB) để không đánh đổi ngược lại.
- Trùng lặp: phần đã inline thường vẫn nằm trong tệp CSS đầy đủ tải sau, gây trùng — chấp nhận được nếu phần inline đủ nhỏ.
Critical CSS đi đôi với việc dọn CSS thừa: nếu tệp gốc chứa hàng nghìn dòng không dùng, công cụ trích vẫn phải lội qua đống đó. Xem thêm cách loại bỏ CSS không dùng để vừa giảm tệp gốc vừa giúp bước trích chính xác hơn. Dùng tab Coverage trong Chrome DevTools để thấy bao nhiêu CSS thực sự được dùng trên khung hình đầu.
Câu hỏi thường gặp
Critical CSS có thay thế việc minify CSS không?
Không. Đây là hai việc bổ sung cho nhau: minify giảm dung lượng tệp, còn critical CSS đổi thời điểm tải để bỏ chặn hiển thị. Nên làm cả hai.
Inline CSS vào head có hại cho cache không?
Phần inline không được cache riêng như một tệp, nên mỗi lần tải trang lại tốn lại vài KB đó. Vì vậy chỉ inline phần tới hạn nhỏ, phần lớn vẫn để ở tệp ngoài có thể cache.
Tôi có cần làm critical CSS thủ công không?
Hầu hết trường hợp nên để công cụ build hoặc plugin cache sinh tự động. Làm tay chỉ hợp với trang rất tĩnh, ít thay đổi.
Nếu site của bạn đang bị PageSpeed cảnh báo “loại bỏ tài nguyên chặn hiển thị” mà chưa biết bắt đầu từ đâu, dịch vụ tối ưu tốc độ website của Web22 có thể giúp dựng quy trình trích và inline tự động cho đúng theme bạn dùng.
