4 min read
Client-Side vs Server-Side Detection for WebP
At time of writing, the highly efficient WebP image format is only supported by Chrome and Opera. The chances are you still want users of other browsers to see your images, so you'll need to detect support and provide a fallback for those lacking.
It basically comes down to 2 separate techniques:
- Server-side detection, by content negotiation
Both methods are reliable and future-proof.
Server-side detection is more scalable and maintainable than client-side detection and is probably the way of the future, but may not suit some environments and use cases.
So, use server-side detection where possible. But use client-side detection if:
- Your CDN or static host doesn't support image content negotiation
- The caching limitations (explained below) are a barrier
The "Save Image As" problem
One of the biggest barriers to deploying WebP today is that users who right click, Save Image As... on a WebP image will end up downloading a file which they can't do anything with. This caused uproar when Facebook tried serving WebP images to a test group of users.
Actually, WebP support in software packages is growing, but it'll be many years before it reaches the ubiquity of JPEG and PNG.
Regardless of which detection method you use, provide an explicit 'safe' download link (to a JPEG / PNG version) for any images users may wish to save. This may be useful anyway, e.g. for providing a high-res version.
Content negotiation via the
This is the technique advocated by Ilya Grigorik and Stephen Konig in their recent Google I/O talk.
Here, the WebP version and fallback version of an image share a single URL.
When browsers request an image, they send an HTTP header to the server to say what MIME types they're expecting in return, which looks something like this for Safari:
i.e. "I'll try and handle anything".
Opera verbosely lists out the image formats it supports:
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Note that there's a specific mention for
image/webp in there.
From version 28, Chrome will do this too – albeit slightly more lazily:
Accept: image/webp, */*;q=0.8
Browsers which don't support WebP won't send the
image/webp part, so you can configure your server to look for this when a request for an image comes in. If it's there, return a WebP, otherwise the fallback: a PNG, JPEG or GIF.
It takes some extra configuration to make this cache-friendly: setting
Vary: Accept on responses to tell caches to treat requests for different
Vary headers as separate resources (so e.g. a request from Safari for an image doesn't return a cached WebP version).
Ilya explains this technique – with examples for Nginx – in his article Deploying WebP Via Accept Content Negotiation.
Problems with server-side detection
Some "black box" hosting environments simply don't offer the level of control required to implement content negotiation.
For example, if your site is hosted on a static or frontend-only service; or you serve your static files from a CDN which doesn't offer this capability.
However, some CDNs – like CDN Connect – have baked-in support for WebP content negotiation.
A cache-friendly server-side implementation depends on adding the
Vary: Accept header to responses, but many caches don't observe this: they simply won't cache assets which carry this header.
This reduced caching benefit trades off against the performance gains of WebP's small file size.
With client-side detection, the WebP version and fallback version of an image have separate URLs – for example with
.jpeg extensions, respectively.
Support for WebP, then image URLs are swapped accordingly.
See my article Using WebP with Modernizr for a full tutorial.
Client-side detection is a great alternative if your platform doesn't allow you to use server-side techniques. Plus it's cache-friendly, because different versions have different URLs.
Problems with client-side detection
<img> tags can result in additional delays before images show. This is because the common practice of putting scripts at the bottom of the
<body> of the page means the whole page must load before the scripts can run – and hence before the images are requested by the browser. Other patterns may not be affected.
The techniques described for CSS images in my article (linked above) perform about about as well as server-side techniques.
You do the work
Server-side detection could eventually become completely transparent – see below.
Client-side detection, however, will always require quite a bit of developer intervention – and hence be harder to maintain.
I'm convinced server-side detection for WebP is the future.
Raster image formats are just compression algorithms. They have different features: colour depth, transparency, animation... but ultimately they all unpack to an RGBA bitmap.
So we could start to consider them a parallel to gzip, but for images instead of text. Many server platforms already transparently gzip text documents when they send them to browsers which support it. How do they know? By the
Accept-Encoding request header. So it makes sense for images to follow a similar pattern (i.e. using the
Developers should be able to push images to their server in whatever format is convenient for them, then let the machines do the work: automatically encode and serve images in the most efficient format the client can handle. Google have been experimenting with this as part of their Page Speed toolkit.
Currently, full transparency is not possible, because the file format is "leaked" to the user by the browser, causing the "Save Image As" problem described above. One step closer to this goal would be for browsers to allow images to be saved off in any format.
But we'll still be waiting for more platforms and CDNs to implement their part of this automated process, and until then many of us will have to convert images ourselves as part of our build process.
We're also waiting for better cache support for
Vary: Accept. Some cache administrators I've talked to have expressed concern that handling
Vary headers correctly could result in an explosion in required cache space, but Ilya argues that this service should be enough of a competitive advantage for CDNs.
Time will tell.
Use WebP now
Start making the most of WebP right now.
Don't wait for the landscape to become perfect, because it might take some time and we could all benefit from faster sites and lower data costs in the meantime. But things will get better, step by step.
Meanwhile, detect on the server if you can. This pattern will scale more easily and ultimately be easier to maintain. Eventually it may disappear altogether, as CDNs start to handle it all for you.
If server-side detection doesn't work for you, go client-side.
Just get on with it and stop making excuses!