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:

  1. Server-side detection, by content negotiation
  2. Client-side detection, using JavaScript

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:

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.

Server-side detection

Content negotiation via the Accept header

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:

Accept: */*

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

Fine-grained control

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.

Client-side detection

With client-side detection, the WebP version and fallback version of an image have separate URLs – for example with .webp and .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

Loading delay

Using JavaScript to switch URLs in <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.

The future

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 Accept header).

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!