Tốc độ website · 6 phút đọc · 2026-09-17
Tối ưu Core Web Vitals WordPress: đo Lighthouse trước và sau
Mình đo Lighthouse trước và sau từng thay đổi trên một bài WordPress. Thêm width và height cho ảnh không đổi CLS; thủ phạm là font của theme, và preload đúng tệp font đưa CLS về 0.

Tối ưu Core Web Vitals WordPress hay bị làm theo danh sách: thêm kích thước ảnh, tắt lazy-load ảnh đầu trang, cài plugin. Mình làm ngược lại: đo trước, đổi đúng một thứ, đo lại. Kết quả có một chỗ trái với danh sách quen thuộc: trên trang thử, thủ phạm CLS không phải cái ảnh.
Site thử là WordPress 7.1 tiếng Việt chạy trên máy (Apache), theme Twenty Twenty-Five. Trang đo là một bài có một đoạn văn và một ảnh 1200×675. Công cụ là Lighthouse 12.8.2, chế độ mobile, chỉ mục Performance. Mỗi biến thể chạy 3 lần; LCP lấy trung vị.
Vì máy chủ nằm ngay trên máy, con số tuyệt đối không giống site thật trên internet. Chỉ nên so các biến thể với nhau.
Cách đo Lighthouse WordPress cho ra số so được
Chạy Lighthouse từ dòng lệnh, lưu JSON để so sau:
npx lighthouse https://ten-mien-cua-ban/duong-dan-bai/ --only-categories=performance --form-factor=mobile --output=json --output-path=lh-1.jsonBa nguyên tắc mình giữ trong mọi lần đo:
- Đổi đúng một thứ mỗi lần. Đổi hai thứ cùng lúc thì không biết thứ nào có tác dụng.
- Chạy mỗi biến thể 3 lần. Điểm dao động giữa các lần chạy, ví dụ 98/97/97 cho cùng một trang không đổi gì.
- Đọc mục chẩn đoán, không chỉ đọc điểm. Điểm 98 không nói gì về chỗ cần sửa.
Trang gốc: CLS 0,030 và một cảnh báo về ảnh
Thẻ ảnh WordPress xuất ra ở trang gốc:
<img decoding="async" src=".../anh-cua-hang.jpg" alt="Ảnh thử 1"/>WordPress tự thêm decoding="async", còn ảnh không có width và height. Kết quả 3 lần chạy:
- Điểm 98/97/97.
- LCP trung vị 2.252 ms.
- CLS 0,030 cả 3 lần.
- TBT 0 ms.
Lighthouse báo "Image elements do not have explicit width and height", và báo tài nguyên chặn hiển thị là /wp-includes/blocks/navigation/style.min.css.

Tối ưu LCP WordPress: ảnh được tải muộn, không phải tải lâu
Phần tử LCP là chính cái ảnh. Lighthouse chia thời gian LCP thành bốn pha. Ở lượt chạy giữa của trang gốc:
- TTFB: 451 ms.
- Load Delay: 1.457 ms.
- Load Time: 11 ms.
- Render Delay: 333 ms.
Bốn con số này đọc thẳng từ tệp JSON đã lưu ở bước đo, bằng lệnh:
node -e "const r=require('./lh-1.json'); for (const p of r.audits['largest-contentful-paint-element'].details.items[1].items) console.log(p.phase, Math.round(p.timing), 'ms')"Ảnh chỉ mất 11 ms để tải, nhưng phải chờ gần 1,5 giây mới bắt đầu tải. Tối ưu dung lượng ảnh sẽ gần như không đổi được con số LCP ở trang này. Chỗ cần nhìn là cái gì khiến trình duyệt phát hiện ảnh muộn.
Thêm width và height: WordPress tự gắn fetchpriority
Mình thêm width="1200" height="675" vào thẻ ảnh. WordPress xuất ra:
<img fetchpriority="high" decoding="async" src=".../anh-cua-hang.jpg" alt="Ảnh thử 1" width="1200" height="675"/>Có kích thước là WordPress tự thêm `fetchpriority="high"`. Kết quả:
- Điểm 98/98/98.
- LCP trung vị 2.252 ms, không đổi.
- Cảnh báo thiếu
width/heightbiến mất. - CLS vẫn 0,030 cả 3 lần.
Lỗi về ảnh đã sửa, mà CLS không nhúc nhích. Nghĩa là cái ảnh không phải thủ phạm.
Có nên lazyload ảnh LCP
Mình thêm loading="lazy" vào chính ảnh đó:
- WordPress bỏ `fetchpriority="high"` khỏi thẻ ảnh.
- Lighthouse báo "Largest Contentful Paint image was lazily loaded".
- Điểm 97/98/98, LCP trung vị 2.176 ms, CLS 0,030.
LCP trên máy thử không tệ đi: 2.176 so với 2.252 ms, nằm trong dao động giữa các lần chạy. Mình không có số chứng minh lazy-load làm chậm LCP ở trang này. Điều chắc chắn đo được là WordPress bỏ fetchpriority và Lighthouse gắn cờ cảnh báo, nên mình vẫn không lazy-load ảnh đầu trang.
Sửa CLS WordPress: tìm đúng thủ phạm
Đọc mục layout-shifts trong báo cáo JSON bằng lệnh:
node -e "const r=require('./lh-1.json'); for (const it of r.audits['layout-shifts'].details.items) console.log(it.score.toFixed(4), it.node.snippet)"Trên trang thử, lệnh chỉ ra đúng một lần xê dịch, điểm 0,0299, ở khối nội dung bài (div.entry-content). Lighthouse ghi hai nguyên nhân khả dĩ:
- "Media element lacking an explicit size".
- Tệp font
themes/twentytwentyfive/assets/fonts/manrope/Manrope-VariableFont_wght.woff2.
Nguyên nhân 1 đã bị loại ở biến thể có width/height: CLS không đổi. Còn lại font Manrope của theme.
Preload font WordPress: CLS về 0
Để kiểm chứng, mình thêm một mu-plugin in thẻ preload cho đúng tệp font đó. Tạo tệp wp-content/mu-plugins/preload-font.php:
<?php
add_action( 'wp_head', function () {
echo '<link rel="preload" href="' . esc_url( get_theme_file_uri( 'assets/fonts/manrope/Manrope-VariableFont_wght.woff2' ) ) . '" as="font" type="font/woff2" crossorigin>' . "\n";
}, 1 );Kiểm thẻ đã có trong trang chưa:
curl -s https://ten-mien-cua-ban/duong-dan-bai/ | grep -o '<link rel="preload"[^>]*Manrope[^>]*>'Đã xong khi lệnh in ra đúng một dòng thẻ preload có chữ Manrope. Kết quả 3 lần chạy:
- CLS 0,000 cả 3 lần.
- Mục layout-shifts không còn lần xê dịch nào.
- Điểm 98/97/97, LCP trung vị 2.402 ms, không tốt lên.

Mã trên chỉ đúng cho theme Twenty Twenty-Five và đúng tệp font đó. Theme khác dùng tệp font khác; tìm tên tệp trong mục layout-shifts của báo cáo trước khi viết thẻ preload.
Tóm tắt bốn biến thể
- Gốc: CLS 0,030, LCP 2.252 ms.
- Thêm width/height: CLS 0,030, LCP 2.252 ms, hết cảnh báo ảnh.
- Lazy-load ảnh LCP: CLS 0,030, LCP 2.176 ms, có cảnh báo lazy-load.
- Preload font: CLS 0,000, LCP 2.402 ms.
Chỉ một thay đổi làm CLS về 0, và đó không phải thay đổi mà danh sách quen thuộc nói tới đầu tiên.
Quy trình tối ưu Core Web Vitals WordPress mình rút ra
Từ bốn biến thể trên, đây là thứ tự mình sẽ làm lại trên một site khác:
- Đo trang gốc 3 lần, lưu cả ba tệp JSON. Ghi điểm, LCP, CLS của từng lần để biết dao động bình thường là bao nhiêu. Ở trang thử, điểm dao động giữa 97 và 98 dù không đổi gì.
- Đọc thủ phạm trước khi sửa. Với CLS, đọc mục layout-shifts. Với LCP, đọc bốn pha để biết thời gian nằm ở chỗ chờ, chỗ tải hay chỗ vẽ.
- Đổi đúng một thứ. Nếu Lighthouse nêu nhiều nguyên nhân khả dĩ, thử loại từng cái, như ở đây mình thêm kích thước ảnh trước để loại nguyên nhân ảnh.
- Đo lại 3 lần. Chỉ coi là có tác dụng khi con số đổi vượt ra ngoài dao động ở bước 1. CLS từ 0,030 về 0,000 cả ba lần là thay đổi rõ; LCP từ 2.252 sang 2.176 ms thì chưa.
- Giữ thay đổi có tác dụng, gỡ thay đổi không có tác dụng. Thêm mã mà không đổi được con số là thêm thứ phải bảo trì.
Làm theo thứ tự này, việc tối ưu Core Web Vitals WordPress trở thành một chuỗi phép thử có kết quả, thay vì một danh sách làm hết cho chắc.
Những gì mình chưa kiểm
- Chưa đo INP; Lighthouse chạy trong phòng thí nghiệm không đo chỉ số này.
- Chưa đo trên máy chủ thật qua internet, chưa có dữ liệu người dùng thật.
- Chưa thử gỡ tài nguyên chặn hiển thị
navigation/style.min.css. - Chưa thử plugin tối ưu nào.
Các chỗ chậm khác như máy chủ, cache, plugin nặng có ở bài Tăng tốc website WordPress: 7 chỗ chậm thường gặp.
- Tác giả
- Quản trị viên
- Đăng ngày
- 2026-09-17
- Cập nhật
- 2026-09-17
- Thời gian đọc
- 6 phút đọc
- Tất cả bài viết