Cache bật rồi web vẫn chậm 1,3 giây vì một lỗi quyền thư mục

Biểu đồ so sánh TTFB trang chủ marketing365.vn khi có cache và khi đi thẳng PHP

Bài viết do Nguyễn Nhật Ánh Dương thực hiện, biên soạn theo Chính sách nội dung của Marketing365. Cập nhật lần cuối .

Nội dung
  1. Triệu chứng duy nhất là TTFB, mọi thứ khác đều xanh
  2. Đo lại hôm nay: cùng một trang chủ, chênh nhau gần một giây
  3. Gốc rễ 1 — lệnh wp-cli chạy bằng root để lại thư mục cache thuộc root
  4. Gốc rễ 2 — chown xong vẫn chưa xong vì hàng đợi preload đã cạn
  5. Bản mobile là file cache riêng, curl -I mặc định không nhìn thấy
  6. Tự kiểm trên site của bạn trong hai phút

Ngày 22/07/2026, marketing365.vn có plugin cache bật sẵn, cấu hình đúng, trang trả về HTTP 200, container Docker chạy bình thường, n8n xanh hết. Không một cảnh báo nào. Nhưng mỗi khách vào trang chủ đều phải chờ PHP dựng lại trang từ đầu, mất 1,24–1,4 giây trước khi nhận được byte đầu tiên.

Chúng tôi vấp đúng lỗi này ba lần trong ba tuần — 22/07, 31/07 và 13/08 — trước khi chấp nhận rằng nó không phải sự cố lẻ. Bài này ghi lại số đo thật của từng lần, hai nguyên nhân độc lập đứng sau nó, và một chỗ chúng tôi tưởng đã sửa xong nhưng thật ra chưa.

Triệu chứng duy nhất là TTFB, mọi thứ khác đều xanh

Ngày 31/07, câu hỏi khởi đầu chỉ đơn giản là “site có ổn không”. Chạy curl lên trang chủ thì thấy hai dòng này:

x-flying-press-cache: MISS
x-flying-press-source: PHP

Đó là toàn bộ manh mối. MISS nghĩa là plugin không tìm thấy file cache cho URL này. source: PHP nghĩa là trang vừa được WordPress dựng lại từ đầu — truy vấn database, chạy theme, chạy plugin — cho đúng một người khách đó, rồi lại dựng tiếp cho người sau.

TTFB hôm ấy: 2,15 giây trên desktop, 1,15 giây trên mobile. Ngoài con số này ra thì không còn triệu chứng nào khác. HTTP 200. SSL còn hạn. Container Up. Log lỗi sạch. Uptime monitor không kêu một tiếng. Đây là kiểu hỏng khó chịu nhất: site vẫn chạy được, chỉ chậm.

Thời điểm hỏng cũng không ai bắt được. mtime của thư mục cache là 27/07 lúc 16:26 — nó đã chết âm thầm suốt bốn ngày, và bốn ngày đó không ai biết.

Đo lại hôm nay: cùng một trang chủ, chênh nhau gần một giây

Để thấy khoảng cách thật chứ không phải con số cũ chép lại từ ghi chép, chúng tôi đo lại lúc 14:28 ngày 14/08/2026, chạy ngay trên VPS đang chứa site. Đo từ máy khác sẽ cộng thêm độ trễ đường truyền và làm nhoè đúng con số cần nhìn.

$ curl -s -o /dev/null -w '%{time_starttransfer}n' https://marketing365.vn/
0.110954
0.114187
0.119422

$ curl -s -o /dev/null -w '%{time_starttransfer}n' 'https://marketing365.vn/?nocache=1'
1.436732
1.000849
1.096178

FlyingPress bỏ qua cache khi URL có query param lạ, nên ?nocache=1 là cách ép một request đi thẳng đường PHP mà không phải xoá cache thật của cả site. Cùng thủ thuật đó trên một bài đơn cho 0,106 giây khi có cache và 0,984 giây khi không.

Sáu cột TTFB đo ngày 14/08/2026: cache HIT 0,106-0,130 giây, đi PHP 0,984-1,437 giây
TTFB trang chủ và một bài đơn của marketing365.vn, đo bằng curl trên chính VPS lúc 14:28 ngày 14/08/2026. Cột xanh là request được cache trả, cột cam là request bị ép đi PHP bằng ?nocache=1.

Chênh lệch 0,87–1,32 giây, trên cùng một máy, trong cùng một phút. Đây là phần thời gian mỗi khách phải trả nếu cache hỏng, và nó cộng thẳng vào FCP với LCP trước cả khi trình duyệt kịp tải dòng CSS đầu tiên.

Gốc rễ 1 — lệnh wp-cli chạy bằng root để lại thư mục cache thuộc root

$ docker exec mna_wp ls -ld /var/www/html/wp-content/cache/flying-press
drwxr-xr-x 2 root root 4096 Jul 27 16:26 /var/www/html/wp-content/cache/flying-press

PHP chạy dưới user www-data. Thư mục thuộc root với quyền 755 nghĩa là www-data đọc được nhưng không ghi được. Plugin vẫn hoạt động, vẫn cố ghi cache, ghi thất bại, và không kêu lấy một dòng log.

Vì sao thư mục đổi chủ? mtime của nó trùng khớp đúng phút chúng tôi chạy lệnh purge:

docker exec mna_wp wp eval 'FlyingPressPurge::purge_everything();' --allow-root

Purge xoá thư mục rồi tạo lại. Tạo lại dưới quyền của tiến trình đang chạy — mà --allow-root nghĩa là root. Không phải lỗi của plugin, là lỗi của chúng tôi.

Đáng nói là cùng một cơ chế đã cắn chúng tôi ở chỗ khác trong chính site này: wp media import --allow-root để lại thư mục uploads/YYYY/MM thuộc root, khiến ảnh upload sau đó hỏng. Lúc ấy chúng tôi coi nó là ca lẻ, không rút thành luật. Nếu rút sớm thì đã không mất bốn ngày cache chết.

Luật giờ nằm trong checklist: sau bất kỳ lệnh wp ... --allow-root nào có tạo file hoặc thư mục, phải trả lại quyền rồi kiểm tra.

docker exec mna_wp chown -R www-data:www-data 
  /var/www/html/wp-content/cache/flying-press

Gốc rễ 2 — chown xong vẫn chưa xong vì hàng đợi preload đã cạn

Đọc thêm: Lỗi Seo Wordpress Thường Gặp Và Cách Kiểm Tra Nhanh

Đây là chỗ bản ghi hôm 22/07 của chúng tôi thiếu, và vì thiếu nên lần 31/07 mất thêm nửa tiếng mò lại.

Sau khi chown, mồi tay hai URL thì cả hai đều HIT. Nhìn qua tưởng xong việc. Nhưng những URL khác vẫn MISS, và số trang cached đứng im ở con số 2. Lý do nằm trong bảng hàng đợi:

$ docker exec mna_wp wp db query 
    "SELECT COUNT(*) FROM wp_flying_press_queue;" --allow-root
0

Bốn ngày ghi cache thất bại đã rút cạn hàng đợi preload. Không còn URL nào chờ thì không có gì được sinh lại. Sửa quyền chỉ mở lại cửa; vẫn phải có người xếp hàng đi vào.

Nạp lại hàng đợi không cần purge — chúng tôi mở src/Preload.php của plugin đọc cho chắc thay vì đoán:

docker exec -u www-data mna_wp 
  wp eval 'FlyingPressPreload::preload_cache();'

Để ý -u www-data chứ không phải --allow-root. Nếu chạy bằng root, chính lệnh đi sửa lỗi sẽ tái tạo đúng cái lỗi vừa sửa.

Kết quả ngày 31/07: 3.004 URL vào hàng đợi, số trang cached từ 2 lên 163, rồi tự tăng khoảng 25 trang mỗi phút mà không cần kích tay — watchdog flying_press_queue_watchdog chạy mỗi phút một lần. TTFB trang chủ từ 2,15 giây xuống 0,11–0,16 giây.

Bài học đo lường ở đây: thấy một URL HIT là chưa đủ để kết luận. Phải kiểm số trang cached có tự tăng hay không.

Bản mobile là file cache riêng, curl -I mặc định không nhìn thấy

Cấu hình của chúng tôi bật cache_mobile, nghĩa là mỗi URL lưu hai file: index.html.gz cho desktop và index-mobile.html.gz cho mobile. Lúc viết bài này, site đang có 1.601 file mỗi loại.

Lần 22/07, sau khi chown, trang chủ HIT trên desktop nhưng vẫn MISS trên mobile, vì chỉ bản desktop kịp sinh. Chúng tôi suýt đóng việc ngay tại đó và đi làm chuyện khác.

curl -I không gửi User-Agent mobile, nên nó chỉ hỏi bản desktop. Muốn kiểm đủ thì phải test cả hai:

MOB="Mozilla/5.0 (Linux; Android 11; moto g power) AppleWebKit/537.36 
Chrome/149 Mobile Safari/537.36"

curl -sI https://marketing365.vn/            | grep x-flying-press
curl -sI -A "$MOB" https://marketing365.vn/  | grep x-flying-press

Đây là loại sai số dễ mắc nhất khi kiểm cache: công cụ trả lời rất đúng câu bạn hỏi, chỉ là bạn hỏi thiếu mất một nửa. Nếu bạn đang kiểm tra tốc độ load web WordPress, hãy đưa User-Agent mobile vào ngay từ lần đo đầu tiên — phía điện thoại gần như luôn là phía chậm hơn.

Tự kiểm trên site của bạn trong hai phút

Ba lệnh, chạy theo đúng thứ tự nhân quả: quyền thư mục, rồi file cache, rồi hàng đợi.

# 1. Header nói gì (nhớ test cả hai User-Agent)
curl -sI https://your-site.com/ | grep -i x-flying-press

# 2. Thư mục cache thuộc về ai, có bao nhiêu file
docker exec mna_wp ls -ld /var/www/html/wp-content/cache/flying-press
docker exec mna_wp find /var/www/html/wp-content/cache/flying-press 
  -name 'index*.html.gz' | wc -l

# 3. Hàng đợi preload còn dòng nào không
docker exec mna_wp wp db query 
  "SELECT COUNT(*) FROM wp_flying_press_queue;" --allow-root

Đọc kết quả:

  • HIT ở cả hai User-Agent, thư mục thuộc www-data, số file tăng dần → không có gì phải làm.
  • MISS + thư mục thuộc root → gốc rễ 1. Chown rồi làm tiếp bước dưới.
  • MISS + thư mục đúng chủ nhưng hàng đợi 0 dòng và số file đứng im → gốc rễ 2. Chạy preload_cache() dưới quyền www-data.
  • MISS ở hai lần curl liên tiếp nhưng hàng đợi vẫn có dòng → bình thường, chờ preload chạy. FlyingPress chỉ chạy pipeline tối ưu cho request mang header X-Flying-Press-Preload; khách thường chỉ được xếp hàng rồi nhận HTML thô.

Lần thứ ba, ngày 13/08, chúng tôi phát hiện ra từ một hướng chẳng liên quan gì: đang chẩn đoán chuyện Google index chậm thì thấy Googlebot ăn TTFB 0,75–0,97 giây, lần theo mới ra thư mục cache lại về tay root. Server chậm thì Google hạ tốc độ bò, và tồn kho trang chưa index phình ra. Triệu chứng nổi lên ở nơi cách xa nguyên nhân hai lớp.

Ba lần trong ba tuần thì với chúng tôi đây là hỏng định kỳ, không phải sự cố. Bước kiểm trên giờ nằm cố định trong quy trình health check của site, chạy sau mỗi phiên có đụng tới wp-cli.

Số liệu tại thời điểm đăng bài, 14/08/2026: thư mục cache thuộc www-data, 1.601 file cache desktop và 1.601 file cache mobile, hàng đợi 0 dòng (đã rút hết, đúng trạng thái bình thường), trang chủ HIT ở cả desktop lẫn mobile, TTFB 0,105–0,134 giây.

Có thể bạn thích

Để lại bình luận