SVG MIME type: image/svg+xml and how to set it
The MIME type of an SVG file is image/svg+xml. What breaks when a server sends another Content-Type, how to check it, and how to set it on common servers.
Published
The MIME type of an SVG file is image/svg+xml. A server must send it in the Content-Type header, or browsers will not draw the file in an <img> tag or a CSS background:
Content-Type: image/svg+xml
There is no shorter spelling. A file labelled image/svg was not drawn in any of the tests below: Chrome treated it as an unknown type and downloaded it.
What the type says
image/svg+xml is registered by the W3C in the SVG specification itself. The parts mean: an image, in the SVG format, written in XML. The registration gives it one optional parameter, charset, so this is valid too:
Content-Type: image/svg+xml; charset=utf-8
The same registration lists the file extension as .svg, and .svgz for an SVG compressed with gzip. Media type, MIME type and content type are three names for the same label.
What happens with the wrong type
An SVG was served from a test server with ten different Content-Type values and loaded in Chrome four ways: in an <img>, as a CSS background, in an <object>, and by opening its address.
| Server sends | <img> and CSS background | <object> | Opened directly |
|---|---|---|---|
image/svg+xml | Drawn | Drawn | Drawn |
image/svg+xml; charset=utf-8 | Drawn | Drawn | Drawn |
text/xml or application/xml | Not drawn | Drawn | Drawn |
text/html | Not drawn | Drawn | Drawn |
text/plain | Not drawn | The code, as text | The code, as text |
| No header at all | Not drawn | The code, as text. Drawn if the file starts with <?xml | The code, as text. Drawn if the file starts with <?xml |
application/octet-stream | Not drawn | Nothing | Downloaded |
image/svg | Not drawn | Nothing | Downloaded |
image/png | Not drawn | Nothing | A broken image |
Two things stand out. An <img> or a background accepts exactly one type. And the file can look fine when its address is typed into the browser yet fail on the page: sent as XML it still draws when opened directly, because the browser reads the XML and finds SVG in it. So opening the file is not a test of the header.
Where the server named a type, Chrome did not guess. The file’s content was a perfectly good SVG in every row, and adding X-Content-Type-Options: nosniff changed none of those results: the label alone decided. The one exception was a reply with no Content-Type at all. There Chrome looked at the content: a file that began with an XML declaration, <?xml version="1.0"?>, was read as XML and drawn in the <object> and when opened directly, though still not in the <img> or the background, and with nosniff added it went back to being shown as text.
How to check what a server sends
From a terminal, ask for the headers only:
curl -I https://example.com/logo.svg
and read the Content-Type line of the reply. In a browser, open the developer tools, go to the Network tab, reload, select the request for the file and read Content-Type under the response headers. From script:
const response = await fetch('/logo.svg');
console.log(response.headers.get('content-type'));
How to set it
Most servers decide the type from the file extension, using a table. Many already have .svg in it.
| Server | Is .svg known by default? | How to add it |
|---|---|---|
| nginx | Yes: the mime.types file in nginx’s source has image/svg+xml svg svgz; | Add that line inside the types { } block if your copy lacks it |
| Apache | Yes: the mime.types file shipped with Apache has image/svg+xml svg svgz | AddType image/svg+xml .svg |
| IIS | Depends on the version’s default list | A mimeMap entry, shown below |
| Amazon S3 | The type is stored with each object when it is uploaded | Set Content-Type on the object |
Python’s http.server | Yes | Nothing to do |
Apache
In the server configuration, a virtual host or an .htaccess file:
AddType image/svg+xml .svg
Apache’s documentation allows AddType in all of those places, and recommends it over editing the mime.types file. .htaccess only takes it if the server permits overrides of the FileInfo kind.
nginx
In the types block, normally kept in the mime.types file that the main configuration includes:
types {
image/svg+xml svg svgz;
}
Reload nginx after changing it.
IIS
IIS refuses to serve a static file whose extension has no MIME mapping. Add one in the site’s web.config, following the form in Microsoft’s documentation for mimeMap:
<configuration>
<system.webServer>
<staticContent>
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
</staticContent>
</system.webServer>
</configuration>
The same can be done in IIS Manager: select the site, open MIME Types and choose Add.
Amazon S3
S3 stores a Content-Type with every object and sends it back unchanged. It is set at upload. The AWS command line guesses the type from the file name by default, and --content-type overrides the guess:
aws s3 cp logo.svg s3://my-bucket/logo.svg --content-type image/svg+xml
An object uploaded with the wrong type keeps it until the object’s metadata is changed or the file is uploaded again.
A server you write yourself
Set the header on the response. In Node:
response.writeHead(200, { 'Content-Type': 'image/svg+xml' });
response.end(svgText);
In PHP, before any output:
header('Content-Type: image/svg+xml');
This is needed whenever a script produces the SVG, such as a chart drawn on request. The address then need not end in .svg at all: the header is what counts.
Compressed SVG
An .svgz file is an SVG compressed with gzip. It has the same type, plus a second header saying it is compressed:
Content-Type: image/svg+xml
Content-Encoding: gzip
In the test, the gzipped file drew with both headers and failed without Content-Encoding. Most servers can compress plain .svg files as they send them, which achieves the same saving with no special files. Is SVG lossy or lossless? covers .svgz.
In JavaScript
The type matters wherever script makes an SVG that is then used as an image.
A Blob. An object URL only works as an image when the Blob is given the type. In Chrome, a Blob of SVG text loaded in an <img> with type: 'image/svg+xml' and failed with text/plain, text/xml or no type:
const blob = new Blob([svgText], { type: 'image/svg+xml' });
image.src = URL.createObjectURL(blob);
A data URI. The type is the first thing after data:. Both of these loaded as images, and data:image/svg, did not:
data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' ...
data:image/svg+xml;base64,PHN2ZyB4bWxucz0i...
The SVG to Base64 tool writes both forms.
Parsing. new DOMParser().parseFromString(text, 'image/svg+xml') parses the text as an SVG document, strictly, as XML.
An uploaded file. A file picked in an <input type="file"> reports its type as file.type. For a file named drawing.svg Chrome gave image/svg+xml, taken from the extension, not from the content. A File made in script has an empty type unless one is passed in. To offer only SVGs in the file picker, use accept="image/svg+xml,.svg".
In HTML
A few elements take the type as a hint. It never overrides what the server sends.
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<object data="/chart.svg" type="image/svg+xml"></object>
<picture>
<source srcset="/logo.svg" type="image/svg+xml">
<img src="/logo.png" alt="Logo">
</picture>
An <img> has no type attribute and needs none. SVG favicon covers the first of these.
Common problems
| What you see | Cause | Fix |
|---|---|---|
The SVG shows when opened directly but not in <img> | The server sends text/xml or application/xml | Send image/svg+xml |
| The browser shows the SVG’s code | Sent as text/plain, or with no type | Send image/svg+xml |
| The browser downloads the SVG instead of showing it | Sent as application/octet-stream or an unknown type | Send image/svg+xml. Also check for a Content-Disposition: attachment header |
| Works on your computer, fails on the server | A file opened from disk is recognised by its extension. The server needs its table | Add the type to the server |
A generated SVG works by address but not in <img> | The script sends its default type, often text/html | Set the header in the script |
| An object URL will not load as an image | The Blob has no type | { type: 'image/svg+xml' } |
| The header is right and the image is still blank | The fault is in the file | See SVG not showing |
Questions
Is it image/svg or image/svg+xml? image/svg+xml. The +xml is part of the name and cannot be left off.
Does an inline SVG need a MIME type? No. SVG written inside an HTML page is part of that page, which is sent as text/html.
Should I add charset=utf-8? It is allowed and harmless. An SVG file can also state its encoding itself, in an XML declaration on its first line.
Does the type affect security? It affects what the file is allowed to do. An SVG can contain script, and it runs when the file is opened directly as image/svg+xml, though never when the file is shown through <img>. Sites that accept SVG uploads from the public should treat them with the same care as HTML.