SVG di Produksi: Performa dan Keamanan 2025 — Kode Minimal, Efek Maksimal
Diterbitkan: 23 Sep 2025 · Waktu baca: 4 mnt · Redaksi Unified Image Tools
TL;DR
- Normalisasi path/transform, konversi teks ke outline jika diperlukan
- Hapus metadata/jejak editor yang tidak perlu, hindari CLS dengan viewBox dan proporsi
- Batasi referensi resource eksternal/script—kelola dengan CSP
Link internal: Gambar Aksesibel Praktis — Batas antara Alt/Dekoratif/Ilustrasi 2025, Desain Thumbnail OGP 2025 — Tidak Terpotong, Ringan, Komunikatif
Pengantar
SVG adalah "grafik vektor yang dideskripsikan teks," menawarkan keuntungan ringan, penskalaan tanpa degradasi, dan aplikasi style yang mudah. Namun, memiliki jebakan seperti node redundan dari software editing, risiko script/referensi eksternal, dan masalah performa dari filter berat. Artikel ini mengorganisir secara komprehensif optimisasi, keamanan, pengiriman dan aksesibilitas untuk biaya minimal, efek maksimal di produksi.
Optimisasi Performa (Diet Struktur dan Koordinat)
- Optimisasi otomatis default dengan SVGO dll. (paksa di CI)
- Konfigurasi precision (mis., 2-3 digit) untuk membulatkan koordinat, aktifkan
convertPathData
/convertTransform
/mergePaths
- Hapus jejak editor dengan
removeMetadata
/removeEditorsNSData
/cleanupIDs
- Konfigurasi precision (mis., 2-3 digit) untuk membulatkan koordinat, aktifkan
- Gunakan
viewBox
sebagai baseline, sesuaikanwidth/height
dengan CSS per use case (responsive) - Set
preserveAspectRatio
secara eksplisit (mis.,xMidYMid meet
) untuk mencegah distorsi/CLS - Jangan over-merge path, gunakan
<symbol>
untuk elemen yang dapat digunakan ulang dan pengiriman sprite - Hindari filter berat/blur/gradien multi-tahap, beralih ke alternatif bitmap atau aproksimasi CSS
Contoh konfigurasi SVGO (svgo.config.js
):
module.exports = {
multipass: true,
js2svg: { indent: 0, pretty: false },
plugins: [
{ name: 'preset-default', params: { overrides: { removeViewBox: false } } },
'cleanupIds',
'convertPathData',
'convertTransform',
{ name: 'removeAttrs', params: { attrs: ['data-*', 'id'] } },
],
};
Strategi Sprite (<symbol> + <use>)
Untuk mengurangi overhead HTTP dan biaya re-render dengan multiple icon, konsolidasi ke satu file di build time untuk caching efektif.
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
<symbol id="icon-search" viewBox="0 0 24 24">...</symbol>
<symbol id="icon-close" viewBox="0 0 24 24">...</symbol>
</svg>
<!-- Penggunaan -->
<svg class="icon" aria-hidden="true"><use href="#icon-search" /></svg>
Catatan: Referensi sprite eksternal (href="/sprite.svg#icon"
) memiliki masalah kompatibilitas CORS/CSP, jadi default gunakan same-origin/inline.
Keamanan dan Sanitasi (Pembatasan XSS/Referensi Eksternal)
SVG memiliki fitur dinamis seperti script/event handler/foreignObject
yang dapat menjadi breeding ground XSS ketika diperlakukan sebagai input data. Tetapkan prinsip-prinsip ini:
- Tolak atribut
script
/foreignObject
/on*
(proteksi ganda dengan build dan sanitizer) - Larang/batasi referensi eksternal (
<use href>
,<image href>
,<link>
) ke same-origin - Blokir
javascript:
dll. di data URL - Tetapkan MIME ke
image/svg+xml
untuk mencegah injeksi konten arbitrer
Contoh DOMPurify (Node/Edge):
import createDOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
export function sanitizeSVG(svg) {
return DOMPurify.sanitize(svg, {
USE_PROFILES: { svg: true },
FORBID_TAGS: ['script', 'foreignObject'],
FORBID_ATTR: ['on*', 'style'], // jika menghindari inline style
ALLOWED_URI_REGEXP: /^(data:image\/(svg\+xml|png|jpeg);|https?:|#)/i,
});
}
Contoh CSP (HTTP header atau <meta httpEquiv>
):
Content-Security-Policy:
default-src 'self';
img-src 'self' data: https:;
object-src 'none';
script-src 'self';
style-src 'self' 'unsafe-inline';
Pengiriman dan Caching (Optimisasi Layer HTTP)
- Content-Type:
image/svg+xml; charset=utf-8
- Aktifkan kompresi teks (Brotli/gzip). Ekstensi
.svgz
biasanya tidak perlu karena biaya operasional - Nama file berversi dengan
Cache-Control: max-age=31536000, immutable
<svg>
inline cepat untuk tampilan awal tapi perhatikan reusability/caching terbatas (icon kecil saja)- Referensi eksternal dikirim same-origin untuk menghindari CORS. SRI umumnya tidak perlu untuk same-origin
SVG inline Next.js (wrapper accessible):
type Props = { title?: string; desc?: string; focusable?: boolean } & React.SVGProps<SVGSVGElement>;
export function Icon(props: Props) {
const { title, desc, focusable = false, ...rest } = props;
const titleId = title ? 'svg-title' : undefined;
const descId = desc ? 'svg-desc' : undefined;
return (
<svg role="img" aria-labelledby={[titleId, descId].filter(Boolean).join(' ') || undefined} focusable={focusable} {...rest}>
{title && <title id={titleId}>{title}</title>}
{desc && <desc id={descId}>{desc}</desc>}
{/* ...path... */}
</svg>
);
}
Aksesibilitas (Semantik dan Manajemen Fokus)
- Grafik bermakna mendapat
<title>
/<desc>
denganrole="img"
, dekoratif menggunakanaria-hidden="true"
/focusable="false"
- Saat mengoutline teks, periksa keterbacaan dan ketajaman outline saat zoom (alternatif hinting)
- Animasi mematuhi
prefers-reduced-motion
, menghormati preferensi pengguna
Fallback dan Alternatif
- Environment legacy mendapat alternatif PNG/WebP (generasi bitmap otomatis di build)
- Gunakan
<picture>
untuk pergantian use case ataunoscript
fail-safe untuk dekorasi non-kritis
<picture>
<source type="image/svg+xml" srcset="/logo.svg" />
<img src="/logo.png" width="200" height="40" alt="Logo situs" />
<noscript><img src="/logo.png" alt="Logo situs" /></noscript>
</picture>
Studi Kasus (Singkat)
Kasus 1: Rendering berat dari output editor mentah
- Gejala: Group berlebihan/presisi koordinat, penggunaan filter berat memperlambat rendering awal
- Solusi: SVGO dengan precision=3,
convertPathData
/mergePaths
, eliminasi filter - Hasil: 42% reduksi file, perbaikan TTI, penggunaan CPU lebih rendah saat re-rendering
Kasus 2: Referensi sprite eksternal diblokir CSP
- Gejala:
<use href="/sprite.svg#icon">
diblokir CSP, icon hilang - Solusi: Beralih ke sprite inline/pengiriman same-origin, relaksasi CSP minimal
- Hasil: Tampilan stabil, efisiensi cache membaik
FAQ
T. Apakah teks harus dioutline?
J. Outline untuk logo di mana integritas bentuk kritis. Jangan outline body text (merugikan keterbacaan/kemampuan terjemah).
T. Bagaimana dengan embed bitmap dengan <image>
?
J. Mungkin tapi perhatikan penanganan referensi eksternal/data URL. Manajemen color space/ukuran menjadi sulit, jadi gunakan hemat.
T. Bagaimana menangani ekspresi filter?
J. Coba alternatif CSS dulu. Jika benar-benar diperlukan, pertimbangkan versi raster resolusi lebih rendah.
Checklist (Untuk Deployment)
- [ ] Paksa SVGO di CI (precision/convertPathData/cleanupIds)
- [ ]
viewBox
/preserveAspectRatio
eksplisit, CLS nol - [ ] Eliminasi filter berat/blur (alternatif raster jika perlu)
- [ ] Larang
script
/foreignObject
/on*
, terapkan sanitasi + CSP - [ ] Pengiriman same-origin,
image/svg+xml
+ kompresi + cache jangka panjang - [ ] Aksesibilitas (title/desc/role/focusable) dan fallback siap