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+xmlDrawnDrawnDrawn
image/svg+xml; charset=utf-8DrawnDrawnDrawn
text/xml or application/xmlNot drawnDrawnDrawn
text/htmlNot drawnDrawnDrawn
text/plainNot drawnThe code, as textThe code, as text
No header at allNot drawnThe code, as text. Drawn if the file starts with <?xmlThe code, as text. Drawn if the file starts with <?xml
application/octet-streamNot drawnNothingDownloaded
image/svgNot drawnNothingDownloaded
image/pngNot drawnNothingA 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.

ServerIs .svg known by default?How to add it
nginxYes: 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
ApacheYes: the mime.types file shipped with Apache has image/svg+xml svg svgzAddType image/svg+xml .svg
IISDepends on the version’s default listA mimeMap entry, shown below
Amazon S3The type is stored with each object when it is uploadedSet Content-Type on the object
Python’s http.serverYesNothing 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 seeCauseFix
The SVG shows when opened directly but not in <img>The server sends text/xml or application/xmlSend image/svg+xml
The browser shows the SVG’s codeSent as text/plain, or with no typeSend image/svg+xml
The browser downloads the SVG instead of showing itSent as application/octet-stream or an unknown typeSend image/svg+xml. Also check for a Content-Disposition: attachment header
Works on your computer, fails on the serverA file opened from disk is recognised by its extension. The server needs its tableAdd the type to the server
A generated SVG works by address but not in <img>The script sends its default type, often text/htmlSet the header in the script
An object URL will not load as an imageThe Blob has no type{ type: 'image/svg+xml' }
The header is right and the image is still blankThe fault is in the fileSee 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.