Cùng một ảnh gốc nhưng bài cũ bị crop gần vuông, bài mới lại ra khung ngang đúng thiết kế. Tình huống này thường xuất hiện sau khi đổi theme, sửa một named size hoặc chuyển cách lưu media. Nhìn qua rất giống lỗi cache, nhưng xóa cache ngay có thể chỉ làm mất dấu vết mà chưa sửa đúng nguyên nhân.

Cách an toàn hơn là đi theo đúng đường của ảnh: file gốc đang ở đâu, Botble đã đăng ký size nào, template gọi tên size gì và trình duyệt cuối cùng tải URL nào. Chỉ khi bốn điểm này khớp, việc tạo lại thumbnail mới đáng làm.

Nhìn triệu chứng trước khi chạy lệnh

“Ảnh sai” thực ra có ít nhất bốn nhóm khác nhau. Nếu URL trả 404, cần kiểm tra file hoặc object có tồn tại và media driver có đúng hay không. Nếu ảnh tải được nhưng bị méo, lỗi có thể nằm ở CSS ép cả chiều rộng lẫn chiều cao. Nếu chỉ ảnh cũ giữ tỉ lệ trước đây, thumbnail chưa được tạo lại là giả thuyết hợp lý. Còn khi URL và kích thước đều đúng nhưng nội dung vẫn cũ, lúc đó mới soi browser cache, CDN hoặc trình tối ưu ảnh của host.

Cây chẩn đoán bốn nhóm lỗi thumbnail: 404, crop sai, size cũ và cache CDN
Bốn triệu chứng nhìn khá giống nhau nhưng cần bằng chứng và cách sửa khác nhau.

Tài liệu Media Management của Botble cũng tách các nhánh khá rõ: kiểm APP_URL, GD/Imagick, media driver và quyền ghi cho lỗi không hiển thị; với ảnh bị stretch hoặc crop sau cập nhật, tài liệu hướng tới việc xác nhận theme, tạo lại thumbnail rồi kiểm cấu hình size. Một lệnh không nên được dùng để chữa tất cả triệu chứng.

Triệu chứng Bằng chứng nên lấy Việc chưa nên làm
URL trả 404 Status, path/object key, media driver Không regenerate trước khi chắc file gốc còn
Ảnh bị méo hoặc crop sai Natural dimensions, CSS box, named size Không mặc định đổ cho cache
Chỉ ảnh cũ giữ size trước Dimensions và checksum trước/sau Không xóa thumbnail cũ trước khi backup
URL đúng nhưng nội dung cũ Response headers, age, ETag, CDN status Không refresh liên tục rồi đoán

Botble lấy kích thước ảnh từ đâu?

Botble cho phép khai báo các size trong cấu hình media hoặc đăng ký từ theme/plugin bằng RvMedia::addSize(). Ví dụ, một theme có thể đăng ký size featured ở kích thước 560 × 380, rồi Blade lấy URL bằng RvMedia::getImageUrl($post->image, 'featured'). Khi đổi size thành 640 × 360, ảnh upload sau đó có thể đi theo cấu hình mới trong khi derivative cũ vẫn còn trên storage.

// Đăng ký trong theme hoặc service provider phù hợp
RvMedia::addSize('featured', 640, 360);

// Template phải gọi đúng cùng một tên
RvMedia::getImageUrl($post->image, 'featured');

Điểm dễ nhầm là tên size và kích thước CSS không phải một thứ. Template có thể gọi đúng featured, nhưng CSS lại ép ảnh vào một box tỉ lệ khác. Ngược lại, CSS có thể ổn nhưng template vẫn gọi thumb hoặc một tên đã bỏ. DevTools sẽ cho biết currentSrc, naturalWidth và naturalHeight; ba giá trị đó đáng tin hơn việc nhìn ảnh bằng mắt.

WebP được hỗ trợ không có nghĩa là tự chuyển đổi

Trang Media Management liệt kê WebP và AVIF trong nhóm định dạng ảnh được hỗ trợ. Điều đó chứng minh media manager có thể nhận và xử lý các file này trong cấu hình phù hợp, nhưng không tự chứng minh rằng mỗi JPG upload lên sẽ có thêm một file .webp. Hai phát biểu này khác nhau.

Tài liệu tối ưu theme của Botble có ví dụ dùng thẻ <picture> với URL WebP. Trước khi áp dụng, hãy kiểm tra object hoặc file WebP thật sự tồn tại và trả đúng Content-Type. Nếu pipeline riêng của dự án tạo WebP thì ghi rõ package, hook hoặc job nào chịu trách nhiệm. Đừng nối thêm .webp vào URL rồi hy vọng đời không như mơ.

curl -I https://example.com/storage/posts/photo.jpg
curl -I https://example.com/storage/posts/photo.webp

Nếu URL thứ hai trả 404, fallback trong <picture> chỉ hoạt động khi markup và trình duyệt xử lý đúng; nó không biến file chưa tồn tại thành file hợp lệ. Đây cũng là lý do bài kiểm tra nên ghi cả tên file, MIME và dimensions thay vì chỉ ghi “WebP bật rồi”.

Một fixture nhỏ cho thấy manifest giúp ích thế nào

Workspace này không có source Botble đầy đủ để dựng đúng một bản cài và không chạy lệnh Artisan trên website thật. Vì vậy, phép thử dưới đây chỉ là fixture ảnh độc lập bằng PHP 8.4.24 và GD trên Windows, dùng để kiểm tra cách lập manifest; nó không được trình bày như kết quả của Botble.

Fixture tạo ba ảnh gốc JPG, PNG và WebP cùng kích thước 1200 × 800. Mỗi ảnh được crop giữa thành size cũ 560 × 380, sau đó tạo derivative mới 640 × 360. Manifest ghi MIME, dimensions, dung lượng và SHA-256 của từng file. Kết quả: cả ba derivative mới có đúng kích thước mới, còn hash của ba ảnh gốc giữ nguyên. Negative control cũng xác nhận không có file WebP tự sinh từ đầu vào JPG.

Bảng manifest ảnh JPG PNG WebP trước và sau khi đổi kích thước thumbnail
Fixture GD độc lập xác nhận derivative đổi từ 560 × 380 sang 640 × 360 trong khi checksum ảnh gốc giữ nguyên; đây không phải kết quả chạy Botble Artisan.

Dung lượng trong fixture không được dùng để kết luận format nào tối ưu hơn vì ảnh tổng hợp, chất lượng encoder và nội dung đều ảnh hưởng kết quả. Giá trị của phép thử nằm ở quy trình: có baseline trước khi thay đổi, có positive control cho size mới, có negative control cho file không nên tồn tại và có checksum để biết ảnh gốc bị đụng tới hay không.

Tạo lại thumbnail với đường lui rõ ràng

Tài liệu Botble hiện hành cung cấp nút Generate thumbnails trong phần Settings → Media và lệnh:

php artisan cms:media:thumbnail:generate

Trước khi chạy trên production, hãy backup database cùng media storage trong cùng một mốc nhất quán. Database biết record nào trỏ tới file nào; storage giữ file thật. Chỉ backup một bên có thể tạo ra một bản phục hồi nhìn đầy đủ trên admin nhưng ảnh lại biến mất.

Trên staging hoặc bản sao dùng driver tương đương, quy trình gọn có thể là:

  1. Xuất manifest ảnh gốc và thumbnail hiện có: path/object key, kích thước, dung lượng và checksum.
  2. Đăng ký một named size mới, rồi upload một ảnh mới làm positive control.
  3. Xác nhận ảnh mới ra đúng size trước khi đụng tới thư viện cũ.
  4. Chạy generate thumbnails và lưu exit code, số file thay đổi cùng lỗi ghi file.
  5. Đối chiếu manifest: ảnh gốc không đổi, derivative cũ được thay đúng mapping, URL public trả 200.
  6. Chỉ xóa backup sau khi đã theo dõi 404 và giao diện đủ lâu.

Với thư viện lớn hoặc object storage, thao tác có thể tốn I/O đáng kể. Không nên bịa một con số thời gian chung. Hãy thử trên một tập đại diện, quan sát tốc độ thực tế, giới hạn CPU/memory và lên cửa sổ bảo trì hoặc queue phù hợp với hệ thống của bạn.

GD, Imagick và cloud storage thay đổi điểm kiểm tra

Botble cho phép chọn GD hoặc Imagick trong Settings → Media. Khi ảnh upload được nhưng thumbnail không sinh, kiểm extension đang bật trong đúng PHP runtime phục vụ ứng dụng, không chỉ CLI. Một server có thể dùng PHP-FPM cho web và một binary PHP khác cho cron; nhìn php -m ở terminal chưa đủ nếu hai môi trường lệch nhau.

Với local storage, kiểm quyền ghi và layout thật trước khi khuyên storage:link. Tài liệu Botble nói public/storage mặc định thường là thư mục thật; chỉ dùng storage:link khi nó thực sự là symlink hoặc dự án chọn layout đó. Với S3, R2, Wasabi hay BunnyCDN, cần thêm object prefix, quyền ghi, metadata và trạng thái CDN. Regenerate thành công vào bucket nhưng HTML vẫn trỏ prefix cũ thì giao diện vẫn sai.

Khi lệnh chạy xong mà giao diện chưa đổi

Mở DevTools Network và reload một trang có ảnh lỗi. Ghi URL cuối sau redirect, status, Content-Type, cache headers và dimensions tải về. Sau đó xem HTML đang dùng src hay srcset; trình duyệt có thể chọn một candidate khác với URL bạn vừa kiểm thủ công.

Nếu origin trả file mới nhưng CDN vẫn trả bản cũ, purge đúng URL hoặc prefix theo nhà cung cấp thay vì xóa toàn bộ cache không có mục tiêu. Nếu cả origin lẫn CDN trả đúng ảnh nhưng layout vẫn méo, quay lại CSS: thuộc tính width, height, aspect-ratio và object-fit mới là nơi cần sửa.

Ở bước này, checklist trong bài SEO kỹ thuật cho blog Botble CMS hữu ích để kiểm URL ảnh, alt và tác động hiển thị. Còn sau deploy, có thể nối lỗi xử lý hoặc 404 vào luồng cảnh báo như bài gửi thông báo lỗi Botble tới Slack, thay vì đợi người đọc báo ảnh hỏng.

Checklist trước khi làm trên production

  • Xác nhận ảnh gốc còn tồn tại và đọc được.
  • Ghi named size mà theme đăng ký và named size mà Blade đang gọi.
  • So natural dimensions với box CSS để loại lỗi layout.
  • Lập manifest và backup đồng bộ database + media.
  • Chạy positive control trên staging hoặc bản sao trước.
  • Không suy diễn tự chuyển JPG sang WebP từ danh sách format hỗ trợ.
  • Đối chiếu origin, CDN, srcset và response headers sau regenerate.

Điểm chốt khá đơn giản: regenerate là bước sửa thumbnail cũ, không phải nút “chữa mọi lỗi ảnh”. Khi đã có URL, dimensions và checksum trước/sau, quyết định trở nên rõ ràng hơn nhiều. Nếu ba thứ đó chưa có, khoan chạy lệnh trên production; thêm năm phút lấy bằng chứng thường rẻ hơn một buổi khôi phục media.