SVG không hiển thị trong trình duyệt? Các nguyên nhân phổ biến
Tại sao SVG của bạn hiển thị biểu tượng ảnh lỗi (hoặc không hiển thị gì cả)
Các tệp SVG đáng lẽ phải dễ sử dụng. Là một định dạng thay thế có khả năng mở rộng vô hạn cho các định dạng raster, chúng thường rất đáng tin cậy. Nhưng khi một tệp SVG từ chối hiển thị, lỗi thường âm thầm và gây khó hiểu. Bạn sẽ thấy biểu tượng ảnh bị hỏng, một hình chữ nhật trắng trống rỗng, hoặc chỉ là một khoảng trống nơi đáng lẽ đồ họa của bạn phải xuất hiện. Một tệp JPEG bị hỏng ít nhất còn hiển thị rõ ràng là bị lỗi; còn một tệp SVG tệ có thể trông hoàn hảo trong trình soạn thảo văn bản nhưng vẫn không hiển thị gì trong Chrome, Firefox, hay Safari. Các vấn đề thường quy về một vài nguyên nhân khác nhau: loại MIME không chính xác từ máy chủ web, XML bị lỗi bên trong tệp, chính sách bảo mật chặn mã nội tuyến, hoặc các vấn đề về kích thước khiến SVG hiển thị với kích thước bằng 0. Mỗi vấn đề lại có một giải pháp khác nhau, và nếu bạn nhầm lẫn chúng, bạn sẽ mất hàng giờ đồng hồ. Hướng dẫn này sẽ đi qua từng nguyên nhân với các cài đặt và thuộc tính cụ thể mà bạn cần kiểm tra, chứ không chỉ là những lời khuyên chung chung kiểu 'hãy xác thực tệp của bạn'. Một điểm khác biệt quan trọng trước khi chúng ta bắt đầu: một số vấn đề về SVG bắt nguồn từ công cụ thiết kế (Illustrator, Figma) trong quá trình xuất, trong khi những vấn đề khác lại xảy ra trong quá trình chuyển đổi tệp. Nếu bạn sử dụng một công cụ trực tuyến để chuyển đổi một tệp sang SVG và nó bị lỗi, đó là một loại vấn đề khác so với một tệp SVG hoàn hảo nhưng máy chủ của bạn lại cấu hình sai. Chúng ta sẽ đảm bảo phân biệt rõ ràng hai trường hợp này.
Sai Loại MIME: Vấn đề phía máy chủ mà hầu hết các nhà phát triển bỏ qua
Khi bạn sử dụng thẻ `<img>` hoặc thuộc tính `background-image` trong CSS để hiển thị SVG, trình duyệt sẽ kiểm tra tiêu đề Content-Type được gửi bởi máy chủ. Nếu tiêu đề đó là `text/plain` hoặc `application/octet-stream` thay vì `image/svg+xml` chính xác, hầu hết các trình duyệt sẽ từ chối hiển thị hình ảnh. Bản thân tệp có thể hoàn hảo. Đây là một trong những nguyên nhân phổ biến nhất khiến SVG bị lỗi trong môi trường sản phẩm (production), và nó như một 'bóng ma' trong quá trình phát triển cục bộ (local development). Tại sao ư? Bởi vì khi làm việc cục bộ, bạn có thể chỉ đơn giản là mở tệp từ ổ đĩa, chứ không phải phục vụ nó qua máy chủ. Vấn đề này chỉ thực sự xuất hiện sau khi bạn triển khai (deploy). Để chẩn đoán vấn đề này, hãy mở DevTools của trình duyệt (F12), chuyển sang tab Network, và tải lại trang. Tìm tệp SVG của bạn trong danh sách yêu cầu, nhấp vào đó, và xem phần Response Headers. Dòng `Content-Type` phải hiển thị `image/svg+xml`. Nếu nó hiển thị bất cứ thứ gì khác, bạn đã tìm thấy vấn đề của mình. Cách khắc phục nằm ở máy chủ, chứ không phải trong tệp. Trên Apache, bạn có thể khắc phục bằng cách thêm `AddType image/svg+xml .svg .svgz` vào tệp `.htaccess` của mình. Đối với người dùng Nginx, hãy thêm `image/svg+xml svg svgz;` bên trong khối `types` của tệp `nginx.conf` của bạn. Nếu bạn đang sử dụng IIS, bạn sẽ cần sử dụng Internet Information Services Manager để thêm ánh xạ loại MIME cho phần mở rộng `.svg` thành `image/svg+xml`. Nếu bạn đang sử dụng một nền tảng được quản lý như Netlify hoặc Vercel, điều này được xử lý trong một tệp cấu hình (`netlify.toml` hoặc `vercel.json`). Ví dụ, cú pháp của Netlify sử dụng một khối `[[headers]]` để đặt `Content-Type` cho một đường dẫn cụ thể. Đây là một sửa lỗi chỉ mất năm phút nhưng có thể giúp bạn tiết kiệm hàng giờ bực bội, nhưng chỉ khi bạn biết cách tìm kiếm trong bảng điều khiển mạng (network panel).
XML bị lỗi: Khi bản thân tệp SVG là vấn đề
Một tệp SVG là một tài liệu XML, có nghĩa là nó tuân theo các quy tắc phân tích cú pháp nghiêm ngặt. Một thẻ đóng bị thiếu, một ký tự ampersand (`&`) không được thoát, hoặc một `id` trùng lặp có thể khiến toàn bộ tệp bị lỗi âm thầm trong một số trình duyệt. Đây là một mẹo nhỏ: nếu SVG của bạn hiển thị trong Chrome nhưng không hiển thị trong Firefox, XML bị lỗi là nghi phạm hàng đầu. Firefox hiển thị lỗi XML rõ ràng hơn nhiều. Để tự mình kiểm tra, hãy kéo tệp SVG trực tiếp vào một cửa sổ Firefox. Nếu XML bị hỏng, Firefox sẽ hiển thị cho bạn một thông báo lỗi rõ ràng, đầy đủ số dòng và cột: 'XML Parsing Error: not well-formed at line 47, column 12.' Đó chính là bản đồ kho báu của bạn. Hãy mở tệp trong một trình soạn thảo văn bản như VS Code và đi đến vị trí chính xác đó. Bạn đang tìm kiếm gì? Thường thì đó là một ký tự ampersand (`&`) trong một liên kết mà đáng lẽ phải được viết là `&`. Hoặc bạn có thể tìm thấy một phần tử `<path>` hoặc `<g>` chưa được đóng. Các vấn đề về mã hóa cũng là một lỗi kinh điển, khi một công cụ thiết kế xuất các ký tự nằm ngoài phạm vi ASCII tiêu chuẩn mà không khai báo UTF-8. Tệp SVG của bạn luôn phải bắt đầu bằng `<?xml version="1.0" encoding="UTF-8"?>` nếu nó chứa bất kỳ ký tự không phải ASCII nào. Các phiên bản cũ hơn của Adobe Illustrator rất thích xuất các tệp với các không gian tên XML độc quyền (proprietary XML namespaces). Mặc dù về mặt kỹ thuật không phải là không hợp lệ, nhưng điều này làm tăng thêm dung lượng không cần thiết và đôi khi có thể gây nhầm lẫn cho các bộ phân tích cú pháp. Chạy các tệp này qua một trình tối ưu hóa như SVGO thường sẽ loại bỏ các không gian tên đó, điều này tốt. Nguy hiểm là nếu giao diện đồ họa bằng cách nào đó phụ thuộc vào các thuộc tính độc quyền đó, trong trường hợp đó việc làm sạch tệp có thể làm hỏng nó một cách bất ngờ. Nếu bạn đã chuyển đổi một tệp sang SVG bằng CocoConvert và đầu ra có lỗi XML, vui lòng cho chúng tôi biết thông qua nút phản hồi trên trang kết quả. Chúng tôi chủ động tìm kiếm và khắc phục những loại lỗi chuyển đổi này.
SVG nội tuyến và xung đột chính sách bảo mật nội dung (CSP)
Dán mã SVG trực tiếp vào HTML của bạn là một kỹ thuật mạnh mẽ, nhưng nó đi kèm với một vấn đề lớn: SVG trở thành một phần của DOM. Điều này có nghĩa là nó giờ đây phải tuân theo Chính sách Bảo mật Nội dung (CSP) của trang bạn. Một CSP nghiêm ngặt có thể âm thầm vô hiệu hóa một phần SVG của bạn, dẫn đến sự nhầm lẫn. Điều này đặc biệt đúng đối với các tệp SVG chứa phần tử `<script>`, khối `<style>` cho hoạt ảnh, hoặc phần tử `<use>` tham chiếu đến các định nghĩa ký hiệu bên ngoài. Một chỉ thị CSP phổ biến như `script-src 'self'` sẽ chặn bất kỳ script nội tuyến nào bên trong SVG của bạn chạy. Một chỉ thị như `img-src 'self'` có thể ngăn SVG tải một hình ảnh bên ngoài được tham chiếu qua `<image href="https://external-domain.com/...">`. Tin tốt ư? Trình duyệt sẽ cho bạn biết chính xác vấn đề là gì. Mở bảng điều khiển nhà phát triển của trình duyệt (tab Console, không phải Network) và tìm các thông báo lỗi màu đỏ. Các vi phạm CSP rất rõ ràng, nêu rõ tài nguyên nào đã bị chặn và chỉ thị chính sách nào chịu trách nhiệm, ví dụ: 'Refused to load the script because it violates the following Content Security Policy directive: script-src self.' Cách bạn khắc phục điều này phụ thuộc vào nhu cầu của SVG của bạn. Bạn có thể thêm `'unsafe-inline'` vào chỉ thị `style-src` của mình, nhưng điều này làm suy yếu chính sách bảo mật của bạn, vì vậy tôi kiên quyết khuyên bạn không nên làm vậy. Một giải pháp tốt hơn nhiều cho các SVG động là chuyển CSS vào một stylesheet bên ngoài và liên kết nó bằng `<?xml-stylesheet type="text/css" href="styles.css"?>`. Đối với các phần tử `<use>` trỏ đến các tệp bên ngoài, bạn sẽ cần phải nhúng các ký hiệu (inline the symbols) hoặc điều chỉnh chính sách `img-src` của bạn để cho phép nguồn gốc đó. Các SVG được tạo bởi CocoConvert hoặc các công cụ tương tự sẽ không có script, vì vậy bạn sẽ không gặp xung đột `script-src` với chúng. Tuy nhiên, xung đột `style-src` vẫn có thể xảy ra nếu SVG đã chuyển đổi sử dụng CSS nội tuyến cho màu sắc hoặc phông chữ.
Cấu hình sai Viewport và ViewBox khiến SVG trở nên vô hình
Đây là loại vấn đề SVG gây bực mình nhất. Trình duyệt hiển thị SVG hoàn hảo, nhưng nó lại hiển thị với kích thước bằng không hoặc ở đâu đó ngoài màn hình. Bạn không thấy biểu tượng ảnh bị hỏng; bạn không thấy gì cả. Bất cứ ai đã từng nhìn chằm chằm vào một khoảng trống trong DevTools, thấy phần tử `<svg>` ở đó nhưng không có gì trên màn hình, đều hiểu nỗi đau đặc biệt này. Điểm mấu chốt là mối quan hệ giữa thuộc tính `viewBox`, định nghĩa hệ tọa độ nội bộ của đồ họa, và các thuộc tính `width` và `height`, định nghĩa không gian mà SVG chiếm trên trang. Khi chúng bị thiếu hoặc không khớp, sự hỗn loạn sẽ xảy ra. Bạn sẽ thường thấy điều này với các tệp xuất từ Figma: một SVG được đặt `width="100%"` và `height="100%"` nhưng lại không có `viewBox`. Nếu bạn đặt SVG đó vào một vùng chứa không có chiều cao rõ ràng, SVG sẽ co lại về chiều cao bằng không. Cách khắc phục là thêm một `viewBox` khớp với kích thước bảng vẽ gốc (ví dụ: `viewBox="0 0 800 600"`) hoặc đặt chiều cao cho phần tử chứa trong CSS của bạn. Một lỗi kinh điển khác là khi dữ liệu đường dẫn (path data) trong SVG được vẽ ở các tọa độ nằm rất xa bên ngoài `viewBox`. Nếu `viewBox` của bạn là `"0 0 100 100"` nhưng dữ liệu đường dẫn của bạn bắt đầu tại `M 500 500`, thì đồ họa đang được vẽ cách màn hình 400 đơn vị. Điều này xảy ra khi bạn di chuyển các hình dạng trong một công cụ thiết kế mà không đặt lại gốc của bảng vẽ. Để khắc phục, hãy quay lại công cụ thiết kế của bạn, chọn tất cả các đối tượng và sử dụng lệnh 'Reset Bounding Box' hoặc lệnh tương đương, sau đó xuất lại. Để chẩn đoán điều này, hãy kiểm tra SVG trong DevTools. Nếu chiều rộng hoặc chiều cao được tính toán của nó là 0, thì vấn đề nằm ở kích thước. Bạn cũng có thể ép buộc hiển thị bằng cách tạm thời thêm `style="border: 1px solid red; width: 200px; height: 200px;"` trực tiếp vào thẻ `<svg>`. Điều này sẽ tạo ra một hộp hiển thị và tiết lộ xem bất kỳ phần nào của đồ họa của bạn có đang xuất hiện hay không.
Lỗi tải phông chữ và tài nguyên bên ngoài bên trong SVG
Một tệp SVG không phải lúc nào cũng độc lập hoàn toàn. Nó có thể tham chiếu đến các tài nguyên bên ngoài như phông chữ, hình ảnh, gradient và bộ lọc. Khi các lời gọi bên ngoài này thất bại, SVG có thể hiển thị một phần hoặc trông hoàn toàn sai, tùy thuộc vào mức độ quan trọng của phần bị thiếu. Lỗi phông chữ là một vấn đề đau đầu thường xuyên. Một SVG chỉ định một phông chữ tùy chỉnh trong một phần tử `<text>` sẽ chuyển về phông chữ mặc định của hệ thống nếu phông chữ đó không có sẵn. Điều này thường không làm hỏng hoàn toàn việc hiển thị, nhưng nó có thể khiến văn bản tràn ra khỏi vùng chứa của nó và làm sai lệch toàn bộ bố cục. Lời khuyên của tôi: luôn chuyển đổi văn bản thành đường viền (outlines/paths) trước khi xuất SVG để sử dụng trên web. Trong Illustrator, đó là Type > Create Outlines; trong Inkscape, đó là Path > Object to Path. Điều này loại bỏ hoàn toàn sự phụ thuộc vào phông chữ và một loạt các vấn đề. Hình ảnh bên ngoài bên trong SVG thậm chí còn khó tính hơn. Một thẻ `<image>` trỏ đến một URL có thể thất bại vì URL đó không hoạt động, vì máy chủ hình ảnh thiếu các tiêu đề CORS phù hợp, hoặc vì CSP chặn nguồn gốc đó. Trình duyệt sẽ không hiển thị biểu tượng lỗi; nó sẽ chỉ hiển thị một khoảng trống nơi đáng lẽ hình ảnh phải xuất hiện. Kiểm tra tab Network của bạn để tìm các yêu cầu có trạng thái 404 hoặc trạng thái 0, điều này thường cho thấy một lỗi chặn CORS hoặc CSP. Nếu bạn đang chuyển đổi một tệp PDF hoặc AI có chứa hình ảnh raster, CocoConvert sẽ nhúng các hình ảnh đó trực tiếp vào SVG dưới dạng URI dữ liệu base64. Điều này giải quyết vấn đề tham chiếu bên ngoài nhưng có thể làm cho tệp SVG trở nên rất lớn. Một tệp PDF với vài bức ảnh có thể dễ dàng trở thành một tệp SVG dung lượng hơn 5MB. Trong những trường hợp đó, chúng tôi sẽ nói thẳng thắn: chuyển đổi sang PNG hoặc WebP là lựa chọn thực tế hơn. Chúng tôi sẽ không giả vờ rằng SVG luôn là câu trả lời đúng.
Khi chuyển đổi là nguồn gốc của vấn đề (và phải làm gì với nó)
Đôi khi vấn đề không phải ở trình duyệt hay máy chủ của bạn. Tệp SVG ngay từ đầu đã bị hỏng, là nạn nhân của một quá trình chuyển đổi tồi tệ. Điều quan trọng là phải nhận ra khả năng này, bởi vì nó có nghĩa là bạn nên ngừng gỡ lỗi môi trường của mình và bắt đầu xem xét kỹ lưỡng tệp. Các lỗi chuyển đổi phổ biến bao gồm dữ liệu đường dẫn với độ chính xác cực cao (hơn 15 chữ số thập phân), làm phình to kích thước tệp và có thể khiến bộ phân tích cú pháp gặp lỗi thời gian chờ; chuyển đổi không gian màu không chính xác từ CMYK sang các giá trị RGB không được hỗ trợ; và mã hóa văn bản bị hỏng nếu tệp nguồn sử dụng các bảng chữ cái không phải Latin. Bạn cũng có thể thấy thiếu khai báo không gian tên khi chuyển đổi từ các định dạng dựa vào XML độc quyền. Nếu bạn nghi ngờ một quá trình chuyển đổi kém chất lượng, cách nhanh nhất để kiểm tra là mở SVG trong Inkscape. Nó miễn phí, đa nền tảng và có một bộ phân tích cú pháp SVG rất 'dễ tính'. Nếu tệp trông đúng trong Inkscape nhưng lại bị lỗi trong Chrome, vấn đề có thể là do trình duyệt cụ thể đó. Nếu nó cũng bị lỗi trong Inkscape, thì quá trình chuyển đổi đã thất bại và đầu ra bị hỏng. CocoConvert sử dụng các thư viện chuyển đổi mạnh mẽ, nhưng không có công cụ tự động nào là hoàn hảo. Một tệp AI cực kỳ phức tạp với hàng trăm lớp, hoặc một tệp PDF phụ thuộc nhiều vào hiệu ứng trong suốt, có thể tạo ra đầu ra SVG không hoàn hảo. Trong những tình huống này, cách đáng tin cậy nhất thường là mở tệp nguồn trong ứng dụng gốc của nó, xuất trực tiếp sang SVG, và sử dụng CocoConvert cho các cặp định dạng mà chúng tôi vượt trội, như chuyển đổi PNG đơn giản sang SVG hoặc SVG sang PDF để in. Nếu bạn nhận được một tệp SVG bị lỗi từ quá trình chuyển đổi, vui lòng báo cáo bằng cách sử dụng biểu mẫu phản hồi trên trang kết quả, và đính kèm tệp gốc nếu bạn có thể. Những ví dụ cụ thể là cách chúng tôi cải thiện quy trình chuyển đổi cho mọi người, và chúng tôi xem xét các báo cáo đó một cách nghiêm túc.