Using SVG with CSS3 and HTML5 — Supplementary Material

Example code and online extras for the O'Reilly Media book by Amelia Bellamy-Royds, Kurt Cagle, and Dudley Storey.

Understanding CORS and SVG

Web browsers are designed to strictly limit features that allow one web page to access another, to protect your privacy.

Each web page the browser downloads from a server may have been customized for you based on log-in or preferences information. If another web site could access that data via a script, it could then pass the information to its web server, and a malicious script could therefore access your private data. This is known as a cross-site scripting attack.

Browsers therefore strictly limit the ability of scripts to access content from other web servers. By default, files can only be accessed from other web pages on the same origin.

A web page origin is defined by the combination of a complete web domain, protocol (e.g., HTTP versus HTTPS), and port number (usually only relevant for local web servers). Any two pages are either same-origin or cross-origin, meaning from different origins.

Note

In many complex modern websites—with many users uploading different files without oversight by a system administrator—even same-origin file access can be a security problem. Although same-origin files are not blocked by default, the newer Content Security Protocol (CSP) HTTP headers can be used to restrict the ability of files to send data to other origins.

If you’re not using JavaScript to fetch files, it’s often easy to ignore cross-origin issues.

For many aspects of web sites, cross-origin files are not restricted. By default, any web page can embed an image, or even an <iframe>, from anywhere on the web. You can also use cross-origin stylesheets and library scripts. (Although Content Security Policy directives can change this, explicitly telling the browser not to allow a page to be embedded in another origin’s page.)

The browser maintains your privacy by trying to limit ways for scripts in the host web page to access the data of the embedded or linked file.

So it surprises many devs when it doesn’t work this simply for SVG.

Many SVG features that use external files do more than just embed them like an image. The referenced SVG file interacts with the current document in ways that can’t easily be shielded from scripts. This includes <use> element clones and also graphical effects like filters, masks, and clipping paths. Since the SVG document from the other origin could have (theoretically) been customized based on private data, they are subject to cross-origin limitations.

Cross-origin restrictions on scripts and embeds have been around since the beginning of JavaScript on the web. However, many scripted applications on the web today rely on the ability to access data from other web servers. How do they do it?

By getting permission.

Cross-origin restrictions exist because the content from the other web server may be sensitive private data. The browser needs permission from that server before it lets another web page access the file.

One web server grants permission to another via HTTP headers sent with the file. These are known as cross-origin headers, or CORS.

Here’s the complicated part: cross-origin headers are only provided by web servers if the browser asks for them. And the browser only asks for them under certain circumstances.

For XMLHttpRequest from JavaScript, the browser calculates whether to ask for permissions automatically: if the URL is on a different origin, it asks for CORS permissions when it requests the file.

Other requests, such as HTML <img>, <script>, stylesheet <link>, and <iframe> sources, usually don’t request cross-origin permissions. The default behavior of these elements doesn’t need special permissions. But if you want to use the same file for something that does require cross-origin permissions, you can tell the browser that it should request them by adding a crossorigin attribute on the HTML element.

Tip

In SVG 2, the SVG <script> and <image> elements also get crossorigin attributes, with the same options and behavior as in HTML (although browser support won’t be as good).

Other contexts, such as images loaded by JavaScript for use in canvas, or <link> preload references, can be made with or without request permissions, depending on what you’re going to ask the browser to do with the file.

But the SVG spec was written before any of these rules were developed.

SVG <use> elements don’t currently have any way to ask for cross-origin permissions. They just don’t work cross-origin, at all.

Tip

There is currently no way for a <use> element to reuse content from another web domain, even if that content is served with cross-origin HTTP headers.

Plans to include cross-origin <use> in SVG 2 were put on hold, until the new shadow DOM model of <use> elements was more consistently implemented in browsers.

If you want to use SVG <use> references to an asset file that you host on a different web domain then your main web pages, the only solution (currently) is JavaScript. A JavaScript XMLHTTPRequest or fetch request can download the file—with cross-origin headers—and inject the markup into your page, where the SVG renderer can access it.

In contrast, new CSS specifications recommend that browsers use “anonymous” cross-origin mode when requesting assets such as SVG masks, filters, and clipping paths, or images that will be used in advanced features such as shape-outside. That means that they will ask for cross-origin permissions, but they won’t send any cookies from previous visits to that web server. The web server must also be configured to specifically allow anonymous CORS.

However, you can still face problems with CORS and CSS assets.

Because servers only send CORS permission headers on request, the browser can’t reuse a cached copy of a file from a previous request, such as an ordinary image download. If it tries, it will treat the file as if the server had sent a no-CORS-allowed response.

If you’re using a separate server (e.g., CDN) to host SVG assets like filters and masks, or images that you want to use in shape-outside, you need a two-pronged approach to avoid bad caches:

Unfortunately, there currently isn’t a way to indicate that normal CSS image requests, such as background-image references, should request cross-origin permissions. You can use an HTML “preload” <link> element to force the browser to fetch the image file, and request cross-origin permissions when it does.

There’s one final complication to consider when using CORS.

Because the cross-origin information is included in the HTTP header, the external files must be served to the web browser using the hypertext protocol—in other words, a URL starting with http: or https:.

The file: protocol normally used to access files from your own computer doesn’t provide cross-origin permissions. When working with web pages saved on your own computer, you will need to run an HTTP development web server on your own machine in order to test scripts that access other files. You’ll need to specifically grant that server access to the folder with the relevant files.

Warning

Firefox allows files saved to your computer to access other local files in the same directory or sub-directories, as if they were on the same web server origin. This is convenient when doing development work, but be careful about opening saved web page files when using Firefox!

Other features that are restricted to same-origin files—like SVG <use> references—are also restricted, in most browsers, for file: URLs. So if you’re developing any SVG that uses multiple files, you’ll definitely want to set up a local test server.