Cloudflare Images was announced one year ago on this very blog to help you solve the problem of delivering images in the right size, right quality and fast. Very fast.
It doesn’t really matter if you only run a personal blog, or a portal with thousands of vendors and millions of end-users. Doesn’t matter if you need one hundred images to be served one thousand times each at most, or if you deal with tens of millions of new, unoptimized, images that you deliver billions of times per month.
We want to remove the complexity of dealing with the need to store, to process, resize, re-encode and serve the images using multiple platforms and vendors.
At the time we wrote:
Images is a single product that stores, resizes, optimizes and serves images. We built Cloudflare Images, so customers of all sizes can build a scalable and affordable image pipeline in minutes.
We supported the most common formats, such as JPG, WebP, PNG and GIF.
We did not feel the need to support SVG files. SVG files are inherently scalable, so there is nothing to resize on the server side before serving them to your audience. One can even argue that SVG files are documents that can generate images through mathematical formulas of vectors and nodes, but are not images per se.
There was also the clear notion that SVG files were a potential risk due to known and well documented vulnerabilities. We knew we could do something from the security angle, but still, why go through that workload if it didn’t make sense in the first place to consider an SVG as a supported format.
Not supporting SVG files, though, did bring a set of challenges to an increasing number of our customers. Some stats already show that around 50% of websites serve SVG files, which matches the pulse we got from talking with many of you, customers and community.
If you relied on SVGs, you had to select a second storage location or a second image platform elsewhere. That commonly resulted in an egress fee when serving an uncached file from that source, and it goes against what we want for our product: one image pipeline to cover all your needs.
We heard loud and clear, and starting from today, you can store and serve SVG files, safely, with Cloudflare Images.
SVG, what is so special about them?
The Scalable Vector Graphics file type is great for serving all kinds of illustrations, charts, logos, and icons.
SVG files don't represent images as pixels, but as geometric shapes (lines, arcs, polygons) that can be drawn with perfect sharpness at any resolution.
Let’s use now a complex image as an example, filled with more than four hundred paths and ten thousand nodes:
Contrary to the bitmaps where pixels arrange together to create the visual perception of an image to the human eye, that vector image can be resized with no quality loss. That happens because resizing that SVG to 300% of its original size is redefining the size of the vectors to 300%, not expanding pixels to 300%.
This becomes evident when we’re dealing with small resolution images.
Here is the 100px width SVG from the Toroid shown above:
and the correspondent 100 pixels width PNG:
Now here is the same SVG with the HTML width attribute set at 300px:
and the same PNG you saw before, but, upscaled by 3x, so the width is also 300px:
The visual quality loss on the PNG is obvious when it gets scaled up.
Keep in mind: The Toroid shown above is stored in an SVG file of 142Kb. And that is a very complex and heavy SVG file already.
Now, if you do want to display a PNG with an original width of 1024px to present a high quality image of the same Toroid above, the size will become an issue:
The new 1024px PNG, however, weighs 344 KB. That’s about 2.4 times the weight of the unique SVG that you could use in any size.
Think about the storage and bandwidth savings when all you need to do with an SVG, to get the exact same displayed image is use a
width=”1024” in your HTML. It requires less than half of the kilobytes used on the PNG.
Couple all of this with the flexibility of using attributes like
viewbox in your HTML code, and you can pan, zoom, crop, scale, all without ever needing anything other than the one and original SVG file.
Here’s an example of an SVG being resized on the client side, with no visual quality loss:
Let’s do a quick summary of what we covered so far: SVG files are wonderful for vector images like illustrations, charts, logos, and are infinitely scalable with no need to resize on the server side;
the same generated image, but on a bitmap is either heavier than the SVG when used in high resolutions, or with very noticeable loss of visual quality when scaled up from a lower resolution.
So, what are the downsides of using SVG files?
Throughout the years, numerous exploits have been known, identified and corrected.
Some old attacks were very rudimentary, yet effective. The famous Billion Laughs exploited how XML uses Entities and declares them in the Document Type Definition, and how it handles recursion.
Entities can be something as simple as a declaration of a text string, or a nested reference to other previous entities.
If you defined a first entity with a simple string, and then created a second entity calling 10 times the first one, and then a third entity calling 10 times the second one up until a 10th one of the same kind, you were requiring a parser to generate an output of a billion strings as defined on the very simple first entity. This would most commonly exhaust resources on the server parsing the XML, and form a DoS. While that particular limitation from the XML parsing got widely addressed through XML parser memory caps and lazy loading of entities, more complex attacks became a regular thing in recent years.
The common themes in these more recent attacks have been XSS (cross-site-scripting) and foreign objects referenced in the XML content. In both cases, using SVG inside <object> tags in your HTML is an invitation for any ill-intended file to reach your end-users. So, what exactly can we do about it and make you trust any SVG file you serve?
The SVG filter
We've developed a filter that simplifies SVG files to only features used for images, so that serving SVG images from any source is just as safe as serving a JPEG or PNG, while preserving SVG's vector graphics capabilities.
- We remove scripting. This prevents SVG files from being used for cross-site scripting attacks. Although browsers don't allow scripts in <img>, they would run scripts when SVG files are opened directly as a top-level document.
- We remove hyperlinks to other documents. This makes SVG files less attractive for SEO spam and phishing.
- We remove references to cross-origin resources. This stops 3rd parties from tracking who is viewing the image.
What's left is just an image.
SVG files can also contain embedded images in other formats, like JPEG and PNG, in the form of Data URLs. We treat these embedded images just like other images that we process, and optimize them too. We don't support SVG files embedded in SVG recursively, though. It does open the door to recursive parsing leading to resource exhaustion on the parser. While the most common browsers are already limiting SVG recursion to one level, the potential to exploit that door led us to not include, at least for now, this capability on our filter.
We do set Content-Security-Policy (CSP) headers in all our HTTP response headers to disable unwanted features, and that alone acts as first defense, but filtering acts in more depth in case these headers are lost (e.g. if the image was saved as a file and served elsewhere).
Our tool is open-source. It's written in Rust and can filter SVG files in a streaming fashion without buffering, so it's fast enough for filtering on the fly.
The SVG format is pretty complex, with lots of features. If there is safe SVG functionality that we don't support yet, you can report issues and contribute to development of the filter.
You can see how the tool actually works by looking at the tests folder in the open-source repository, where a sample unfiltered XML and the already filtered version are present.
Here’s how a diff of those files looks like:
Removed are the external references, foreignObjects and any other potential threats.
How you can use SVG files in Cloudflare Images
Starting now you can upload SVG files to Cloudflare Images and serve them at will. Uploading the images can be done like for any other supported format, via UI or API.
Variants, named or flexible, are intended to transform bitmap (raster) images into whatever size you want to serve them.
SVG files, as vector images, do not require resizing inside the Images pipeline.
This results in a banner with the following message when you’re previewing an SVG in the UI:
And as a result, all variants listed will show the exact same image in the exact same dimensions.
Because an image is worth a thousand words, especially when trying to describe behaviors, here is what will it look like if you scroll through the variants preview:
With Cloudflare Images you do get a default Public Variant listed when you start using the product, and so you can immediately start serving your SVG files using it, just like this:
And, as shown from above, you can use any of your variant names to serve the image, as it won’t affect the output at all.
If you’re an Image Resizing customer, you can also benefit from serving your files with our tool. Make sure you head to the Developer Documentation pages to see how.
You can subscribe to Cloudflare Images directly in the dashboard, and starting from today you can use the product to store and serve SVG files.
If you want to contribute to further developments of the filtering too and help expand its abilities, check out our SVG-Hush Tool repo.
You can also connect directly with the team in our Cloudflare Developers Discord Server.