WebP is both a lossless image format, like PNG, which preserves every detail of the original image, and a lossy format, like JPG, which will compress the original image down to a certain quality.

WebP support in browsers such as Firefox and IE is virtually nonexistent. However if a big chunk of your audience browses from Chrome, Android Browser or Opera, you could serve WebP today and reap the benefits.

The benchmark

So how much better is WebP actually? Here’s a quick test to find out.

cwebp from the official WebP distribution was used to convert images. The files used by this benchmark can be found in this git repo.

The source is a lossless 24-bit PNG, optimized with optipng.
Lossless conversion from PNG to WEBP:

cwebp -lossless source.png -o lossless.webp

PNG: 532 KB
WEBP: 386 KB
Webp is 28% smaller.

For comparison I used a JPG image saved at 95% quality.
Lossy conversion to WEBP:

cwebp -q 95 source.png -o lossy.webp

JPG is 243 KB.
WEBP is 160 KB.
Webp is 35% smaller.

Bandwidth savings offered by Google’s new image format can be significant enough to be worth the trouble.

Serving WebP

Every browser (AKA user agent) will send an Accept header with each HTTP request. Serving WebP, or any other content actually, can be done in a browser neutral manner by looking at this header and responding with a Content-Type known to the user agent. This is the basic principle of content negotiation.

Chrome advertises its WebP support in a few ways:

For HTML files:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

For image files:

Accept: image/webp,*/*;q=0.8

So all we have to do is to match against the image/webp part in our web server config and return the appropriate file.

There’s another small trick which we will employ in this example. Every image linked from a HTML document will be linked without a file extension.

That can’t be right, can it?

It is actually right from a REST point of view. The image is a resource and its content type should not be included in the resource path. Content negotiation should be reserved for the Accept header.

Setting up nginx

A very basic nginx configuration would be:

location /webp/img {

    add_header Vary Accept;

    if ($http_accept ~* "image\/webp") {
        rewrite (.*) $1.webp break;

    try_files $uri.png $uri.jpg

This assumes that the location will only serve images. The Vary header is used to instruct proxies that the content type can differ between server responses. The try_files directive is used in order to fallback to PNG or JPG images.
This setup does not cover cases where nginx is used as a proxy. You can read more on that in this article.

Ok, but how about saving images? Chrome does another smart thing. If you try to save a WebP image, it will give you the JPG or the PNG version instead, so that it can be viewed on any device.

More on WebP and content negotiation can be found below.