You can use SVG images safely on a website by using them for logos, icons, and simple illustrations, embedding untrusted files only with <img> (where scripts don't run), and sanitizing any SVG a user uploads before you store or display it. Treat SVG as code, not as a picture, and most of the risk goes away.
When should you use SVG instead of PNG or JPEG?
SVG (Scalable Vector Graphics) describes an image as shapes, paths, and text in XML. The browser draws it at whatever size you need, so it stays sharp on any screen density. There's no 1x or 2x file to manage, which our post on Retina and HiDPI images explains in more detail.
Good fits:
- Logos and wordmarks
- UI icons
- Flat illustrations and spot art
- Charts, maps, and diagrams
- Simple animations
Poor fits are photos and anything with lots of texture, gradients, or noise. An SVG trace of a photo is usually huge and still looks worse than a JPEG. For those, pick a raster format. Our WebP vs AVIF vs JPEG vs PNG comparison will help.
A quick check: open the SVG in a text editor. If it's a few hundred lines of clean paths, great. If it's megabytes of tiny paths or has a base64 PNG embedded inside, it's really a raster image in disguise.
What are the ways to embed SVG, and what does each allow?
This is where most confusion comes from. The same file behaves differently depending on how it lands on the page.
| Method | Scripts run? | Style with page CSS? | Cached separately? | Good for |
|---|---|---|---|---|
<img src="logo.svg"> | No | No | Yes | Logos, illustrations, any untrusted SVG |
Inline <svg> in the HTML | Yes, as part of your page | Yes, including currentColor | No, it ships with the HTML | Icons, charts, anything you want to color or animate |
CSS background-image | No | No | Yes | Decorative patterns and shapes |
<object> or <iframe> | Yes, inside its own document | No, only CSS inside the file | Yes | Interactive SVG you fully trust |
SVG in <img> and CSS backgrounds
MDN's page on SVG as an image lists the restrictions browsers apply in image contexts. JavaScript is disabled. External resources like other images or stylesheets can't be loaded unless they're inlined as data: URLs. The same rules cover CSS background-image.
This makes <img> the safest default. The trade-off is that your page CSS can't reach inside, so you can't recolor a single path on hover.
<img src="/img/logo.svg" width="180" height="40" alt="Acme Hardware">
Inline SVG
Inline SVG is part of your DOM. You can style it with CSS, animate it, and set fill="currentColor" so an icon picks up the text color around it. That's why most icon systems use inline SVG.
<button>
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
<path fill="currentColor" d="M8 1l2 5h5l-4 3 2 6-5-4-5 4 2-6-4-3h5z"/>
</svg>
Save
</button>
The flip side: anything in that SVG runs with your page's full permissions. Only inline SVG you wrote or have sanitized.
<object> and <iframe>
These load the SVG as its own document. MDN notes that the image-context restrictions don't apply here, so scripts inside the file do run. Use them for interactive diagrams you control. Never use them for untrusted files.
How do you make SVG images accessible?
It depends on whether the image carries meaning.
For an SVG in <img>, the rules are the same as any image: write useful alt text, or use alt="" if it's decorative. Our alt text guide covers what to write.
For meaningful inline SVG:
- Add
role="img"to the<svg>element so assistive tech treats it as one image instead of a pile of shapes. - Give it a name with
aria-label, or add a<title>as the first child and point to it witharia-labelledby. - For charts, add a longer text description nearby or a data table. A title alone won't explain a trend.
<svg role="img" aria-labelledby="chart-title" viewBox="0 0 400 200">
<title id="chart-title">Monthly signups rose from 120 in May to 310 in August</title>
...
</svg>
For decorative inline SVG, like an icon next to a text label, add aria-hidden="true". That keeps screen readers from announcing it twice or reading out nonsense.
Also check contrast. An icon drawn with currentColor in a light gray can fail contrast even when the text beside it passes, especially in dark mode.
How do you optimize SVG files?
Design tools export a lot of junk: editor metadata, hidden layers, long decimal numbers, and empty groups. SVGO is the standard open-source optimizer, and it often cuts file size a lot without any visible change.
A few things to watch:
- Keep the
viewBox. Without it, the SVG can't scale to fit its container with CSS. SVGO v4 no longer removesviewBoxby default, but older versions did, and plenty of old configs and plugins still strip it. Check your output. - Keep titles you rely on. If you use
<title>for accessibility, make sure your optimizer settings don't remove it. - Watch IDs on inline SVG. Two inline SVGs with the same gradient ID on one page can clash. SVGO can prefix IDs to avoid this.
- Serve it compressed. SVG is text, so gzip or Brotli from your server shrinks it further.
For raster images, see our post on compression quality settings instead. Different tools, different rules.
Is it safe to let users upload SVG files?
Not without extra work. An SVG is an XML document. It can contain <script> tags, event handler attributes like onload, links with javascript: URLs, <foreignObject> with embedded HTML, and references to external resources that can track who viewed it.
If someone uploads that file and a visitor opens its URL directly, the browser renders it as a document on your domain. Scripts then run with access to your origin, which is a classic stored cross-site scripting (XSS) hole. <img> embedding is safe because scripts are off there, but you can't stop people from opening the file URL in a new tab.
Here's a sensible order of defenses. Use several, not one:
- Decide if you need SVG uploads at all. For avatars and photos, accept only PNG, JPEG, WebP, or AVIF. Many sites convert uploaded SVG to PNG on the server and throw the original away.
- Sanitize on the server. Strip scripts, event handlers,
foreignObject, and external references before you store the file. DOMPurify supports SVG with itssvgprofile and can run in Node with jsdom. Other languages have their own SVG sanitizers. Keep whichever you use up to date. - Sanitize again on the client if you ever insert user SVG inline. Run it through DOMPurify right before it touches the DOM.
- Serve uploads with strict headers. Send
Content-Type: image/svg+xml,X-Content-Type-Options: nosniff, and a restrictiveContent-Security-Policysuch asdefault-src 'none'; style-src 'unsafe-inline'; sandbox. The CSPsandboxdirective blocks scripts even when the file is opened directly. AddingContent-Disposition: attachmentmakes the browser download the file instead of rendering it, which is fine for download links but not for images you display. - Host user files on a separate domain. OWASP's file upload guidance recommends storing uploads on a different host. If a script does slip through, it runs on something like
usercontent.example, away from your login cookies.
Content-Type: image/svg+xml
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox
The same concerns apply to SVG favicons, just on a smaller scale. Our guide on favicon security and tracking covers the icon-specific side.
Can you use SVG for favicons and social images?
Favicons, yes, with fallbacks. Modern Chrome, Firefox, and Edge accept an SVG favicon, and it can even switch colors for dark mode with a media query inside the file. Safari was late to support it and older versions still don't, so you still ship PNG and ICO versions. Our SVG favicon guide has the markup, and the SVG to favicon converter builds the full set of fallback sizes from one SVG.
Social images, no. X's card documentation lists JPG, PNG, WebP, and GIF as supported, and SVG isn't among them. Other platforms are commonly reported to ignore SVG too. Export a PNG or JPEG for og:image. Our Open Graph image size guide has the dimensions.
Google Search, for what it's worth, does index SVG in <img> tags. It lists SVG among its supported image formats.
Common mistakes
- Accepting SVG uploads with no sanitizing. The file extension check won't save you.
- Pasting a downloaded SVG inline without reading it. Icon packs are usually fine. Random files from the internet may not be.
- Stripping
viewBox. The icon then refuses to resize. - Using
<object>for untrusted files. Scripts run there. - Serving SVG with the wrong
Content-Type.text/xmlortext/plaincan stop it rendering as an image. - Giving decorative icons a label. Screen reader users hear "star icon, Save" when "Save" was enough.
- Using SVG for
og:image. Your link previews go blank.
TL;DR
Use SVG for logos, icons, and diagrams, and raster formats for photos. Embed untrusted SVG only with <img>, keep inline SVG for files you trust, and give meaningful SVGs an accessible name. If users can upload SVG, sanitize it on the server, serve it with nosniff and a sandboxing CSP, and host it on a separate domain.
