Use WebP images

Katie Hempenius
Katie Hempenius

Why should you care?

WebP images are smaller than their JPEG and PNG counterparts—usually on the magnitude of a 25–35% reduction in filesize. This decreases page sizes and improves performance.

  • YouTube found that switching to WebP thumbnails resulted in 10% faster page loads.
  • Facebook experienced a 25-35% filesize savings for JPEGs and an 80% filesize savings for PNGs when they switched to using WebP.

WebP is an excellent replacement for JPEG, PNG, and GIF images. In addition, WebP offers both lossless and lossy compression. In lossless compression no data is lost. Lossy compression reduces file size, but at the expense of possibly reducing image quality.

Convert images to WebP

People generally use one of the following approaches for converting their images to WebP: the cwebp command-line tool or the Imagemin WebP plugin (npm package). The Imagemin WebP plugin is generally the best choice if your project uses build scripts or build tools (e.g. Webpack or Gulp), whereas the CLI is a good choice for small projects or if you'll only need to convert images once.

When you convert images to WebP, you have the option to set a wide variety of compression settings—but for most people the only thing you'll ever need to care about is the quality setting. You can specify a quality level from 0 (worst) to 100 (best). It's worth playing around with this, find which level is the right tradeoff between image quality and filesize for your needs.

Use cwebp

Convert a single file, using cwebp's default compression settings:

cwebp images/flower.jpg -o images/flower.webp

Convert a single file, using a quality level of 50:

cwebp -q 50 images/flower.jpg -o images/flower.webp

Convert all files in a directory:

for file in images/*; do cwebp "$file" -o "${file%.*}.webp"; done

Use Imagemin

The Imagemin WebP plugin can be used by itself or with your favorite build tool (Webpack/Gulp/Grunt/etc.). This usually involves adding ~10 lines of code to a build script or the configuration file for your build tool. Here are examples of how to do that for Webpack, Gulp, and Grunt.

If you are not using one of those build tools, you can use Imagemin by itself as a Node script. This script will convert the files in the images directory and save them in the compressed_images directory.

const imagemin = require('imagemin');
const imageminWebp = require('imagemin-webp');

imagemin(['images/*'], {
  destination: 'compressed_images',
  plugins: [imageminWebp({quality: 50})]
}).then(() => {
  console.log('Done!');
});

Serve WebP images

If your site only supports WebP compatible browsers, you can stop reading. Otherwise, serve WebP to newer browsers and a fallback image to older browsers:

Before:

<img src="flower.jpg" alt="">

After:

<picture>
  <source type="image/webp" srcset="flower.webp">
  <source type="image/jpeg" srcset="flower.jpg">
  <img src="flower.jpg" alt="">
</picture>

The <picture>, <source>, and <img> tags, including how they are ordered relative to each other, all interact to achieve this end result.

<picture>

The <picture> tag provides a wrapper for zero or more <source> tags and one <img> tag.

<source>

The <source> tag specifies a media resource.

The browser uses the first listed source that's in a format it supports. If the browser does not support any of the formats listed in the <source> tags, it falls back to loading the image specified by the <img> tag.

<img>

The <img> tag is what makes this code work on browsers that don't support the <picture> tag. If a browser does not support the <picture> tag, it will ignore the tags it doesn't support. Thus, it only "sees" the <img src="flower.jpg" alt=""> tag and loads that image.

Reading the HTTP Accept header

If you have an application backend or web server that lets you rewrite requests, you can read the value of the HTTP Accept header, which will advertise what alternative image formats are supported:

Accept: image/webp,image/svg+xml,image/*,*/*;q=0.8

Reading this request header and rewriting the response based on its contents has the benefit of simplifying your image markup. <picture> markup can get rather long with many sources. Following is an Apache mod_rewrite rule that can serve WebP alternates:

RewriteEngine On
RewriteCond %{HTTP:Accept} image/webp [NC]
RewriteCond %{HTTP:Content-Disposition} !attachment [NC]
RewriteCond %{DOCUMENT_ROOT}/$1.webp -f [NC]
RewriteRule (.+)\.(png|jpe?g)$ $1.webp [T=image/webp,L]

If you go this route, you'll need to set the HTTP Vary response header to ensure caches will understand that the image may be served differently depending on the Accept header:

<FilesMatch ".(jpe?g|png)$">
  <IfModule mod_headers.c>
    Header set Vary "Accept"
  </IfModule>
</FilesMatch>

The previous rewrite rule will look for a WebP version of any requested JPEG or PNG image. If a WebP alternate is found, it will be served with the proper Content-Type header. This will allow you to use image markup similar to the following with automatic WebP support:

<img src="flower-320w.jpg" srcset="flower-320w.jpg 320w, flower-640w.jpg 640w, flower-960w.jpg 960w">

Verify WebP usage

Lighthouse can be used to verify that all images on your site are being served using WebP. Run the Lighthouse Performance Audit (Lighthouse > Options > Performance) and look for the results of the Serve images in next-gen formats audit. Lighthouse will list any images that are not being served in WebP.