Conditionally Serving WebP Images With Nginx

Introduction

This page was created to demonstrate how JPG/PNG images converted to WebP format via optimise-images.sh batch image converter tool can be served conditionally via Nginx web server to web browsers that support WebP image formats. See forum thread for the WebP conversion details. The optimise-images.sh batch conversion and resizer tool now also has a new option - optimise-webp-nginx mode which allows the below Nginx config syntax to be auto generated by your defined web directory path containing your JPG/PNG and WebP images as well as can auto generate a static html gallery comparing side by side your optimised resized JPG/PNG with your WebP converted image.

Serving WebP Images With Nginx

WebP format images are usually much smaller in size than JPG/PNG files (upto 80% smaller) allowing web pages to load faster and are only supported by some web browsers like Google Chrome and Opera. So for non supported browsers like Microsoft Internet Explorer/Edge, Mozilla Firefox and Safari, we need to fallback to the standard JPG or PNG format based images. Below guide is for Nginx users who are not using Nginx ngx_pagespeed module which has filters which allow on the fly auto conversion of JPG, PNG and GIF files to WebP format for just web browsers that support it. You can read up on benefits of ngx_pagespeed module here.

Step 1. In order for Nginx to conditionally serve WebP format images to only web browsers that support it, we can setup a conditional Nginx map which inspects the client's web browser Accept Header for WebP mime type. In nginx.conf i.e. for Centmin Mod Nginx servers at /usr/local/nginx/conf/nginx.conf, add an includes file within http{} server context like below:

include /usr/local/nginx/conf/webp.conf;

Step 2. Within /usr/local/nginx/conf/webp.conf add the following contents:

map $http_accept $webp_extension {
    default "";
    "~*webp" ".webp";
}

Step 3. Within your Nginx vhost's location context i.e. /webp, you can add the following - remove the noindex/nofollow header if you want the page to directory /webp to be indexed by search engines and remove autoindex if you don't want directory contents to be viewable without a index page. For Nginx ngx_pagespeed users, you may have enabled JPG/PNG auto conversion to WebP filter which may affect the below setup, so you would need to uncomment the pagespeed off line by removing the front hash to disable ngx_pagespeed filter within /webp directory.

location /webp {
  #pagespeed off;
  autoindex on;
  add_header X-Robots-Tag "noindex, nofollow";
  location ~* ^/webp/.+\.(png|jpe?g)$ {
    expires 30d;
    add_header Vary "Accept-Encoding";
    add_header Cache-Control "public, no-transform";
    try_files $uri$webp_extension $uri =404;
  }
}

The Cache-Control header's no-transform is explained here:

No transformations or conversions should made to the resource. The Content-Encoding, Content-Range, Content-Type headers must not be modified by a proxy. A non- transparent proxy might, for example, convert between image formats in order to save cache space or to reduce the amount of traffic on a slow link. The no-transform directive disallows this.

Step 4. Restart Nginx server for changes to take affect:

service nginx restart

Demo WebP Images

Below is a demo of PNG embedded image, bees.png which has corresponding bees.png.webp format image in same directory. If this page is viewed in a web browser that supports WebP and is served from a web server like Nginx with above configuration, you will see the image mime type reported as WebP instead of PNG while the image extension remains as displaying bees.png. Otherwise, if served from a web server without such a configuration, you will see the original bees.png image being served instead with mime type reported as PNG.

Original bees.png image file size is 177,424 bytes (173.27 KB) and bees.png.webp optimised file size is 10,520 bytes (10.27 KB)

webp demo

Opera & Chrome web browser users will see the WebP version (Mime Type = WebP)

opera devtools

While unsupported web browser like Firefox, IE11/Edge and Safari users will see the PNG version (Mime Type = PNG)

firefox devtools

WebP Comparison Gallery

Below is example of optimise-images.sh batch conversion and resizer tool's new option - optimise-webp-nginx mode's auto generated static html gallery comparing your optimised and resized JPG/PNG images side by side with WebP converted copy.

------------------------------------------------------------------------------
image profile
image name : width : height : quality : transparency : image depth (bits) : size : user: group
------------------------------------------------------------------------------
images in /usr/local/nginx/html/webp
logged at /home/optimise-logs/profile-log-010517-115230.log
------------------------------------------------------------------------------
image : bees.png : 444 : 258 : 92 : False : 8 : 175296 : root : nginx
image : bees.png.webp : 444 : 258 : 92 : False : 8 : 10520 : root : nginx
image : dslr_canon_eos_m6_1.jpg : 1200 : 800 : 82 : False : 8 : 161086 : root : nginx
image : dslr_canon_eos_m6_1.jpg.webp : 1200 : 800 : 92 : False : 8 : 61544 : root : nginx
image : dslr_nikon_d7200_1.jpg : 2048 : 1365 : 82 : False : 8 : 374954 : root : nginx
image : dslr_nikon_d7200_1.jpg.webp : 2048 : 1365 : 92 : False : 8 : 173414 : root : nginx
image : dslr_nikon_d7200_2.jpg : 1365 : 2048 : 82 : False : 8 : 516224 : root : nginx
image : dslr_nikon_d7200_2.jpg.webp : 1365 : 2048 : 92 : False : 8 : 212754 : root : nginx
image : png24-image1.png : 600 : 400 : 92 : False : 8 : 386063 : root : nginx
image : png24-image1.png.webp : 600 : 400 : 92 : False : 8 : 27104 : root : nginx
image : png24-interlaced-image1.png : 600 : 400 : 92 : False : 8 : 443931 : root : nginx
image : png24-interlaced-image1.png.webp : 600 : 400 : 92 : False : 8 : 27104 : root : nginx
image : samsung_s7_mobile_1.jpg : 2048 : 1536 : 82 : False : 8 : 256253 : root : nginx
image : samsung_s7_mobile_1.jpg.webp : 2048 : 1536 : 92 : False : 8 : 69490 : root : nginx
image : webp-study-source-firebreathing.png : 1024 : 752 : 92 : False : 8 : 1194091 : root : nginx
image : webp-study-source-firebreathing.png.webp : 1024 : 752 : 92 : False : 8 : 71860 : root : nginx

------------------------------------------------------------------------------
Original or Existing Images:
------------------------------------------------------------------------------
| Avg width | Avg height | Avg quality | Avg size   | Total size (Bytes) | Total size (KB) |
| --------- | ---------- | ----------- | --------   | ------------------ | --------------- |
| 1166      | 945        | 87          | 438487     | 3507898            | 3426            |

------------------------------------------------------------------------------
Optimised WebP Images:
------------------------------------------------------------------------------
| Avg width | Avg height | Avg quality | Avg size   | Total size (Bytes) | Total size (KB) |
| --------- | ---------- | ----------- | --------   | ------------------ | --------------- |
| 1166      | 945        | 92          | 81724      | 653790             | 638             |

When resizing is disabled to work with original images via IMAGICK_RESIZE='n'

------------------------------------------------------------------------------
image profile
image name : width : height : quality : transparency : image depth (bits) : size : user: group
------------------------------------------------------------------------------
images in /home/nginx/domains/domain.com/public/images
logged at /home/optimise-logs/profile-log-010517-114117.log
------------------------------------------------------------------------------
image : bees.png : 444 : 258 : 92 : False : 8 : 175467 : root : nginx
image : bees.png.webp : 444 : 258 : 92 : False : 8 : 10520 : root : nginx
image : dslr_canon_eos_m6_1.jpg : 1200 : 800 : 82 : False : 8 : 164947 : root : nginx
image : dslr_canon_eos_m6_1.jpg.webp : 1200 : 800 : 92 : False : 8 : 61544 : root : nginx
image : dslr_nikon_d7200_1.jpg : 6000 : 4000 : 82 : False : 8 : 3701729 : root : nginx
image : dslr_nikon_d7200_1.jpg.webp : 6000 : 4000 : 92 : False : 8 : 1639822 : root : nginx
image : dslr_nikon_d7200_2.jpg : 4000 : 6000 : 82 : False : 8 : 3177146 : root : nginx
image : dslr_nikon_d7200_2.jpg.webp : 4000 : 6000 : 92 : False : 8 : 1094418 : root : nginx
image : png24-image1.png : 600 : 400 : 92 : False : 8 : 387519 : root : nginx
image : png24-image1.png.webp : 600 : 400 : 92 : False : 8 : 27104 : root : nginx
image : png24-interlaced-image1.png : 600 : 400 : 92 : False : 8 : 444852 : root : nginx
image : png24-interlaced-image1.png.webp : 600 : 400 : 92 : False : 8 : 27104 : root : nginx
image : samsung_s7_mobile_1.jpg : 4032 : 3024 : 82 : False : 8 : 1106190 : root : nginx
image : samsung_s7_mobile_1.jpg.webp : 4032 : 3024 : 92 : False : 8 : 214324 : root : nginx
image : webp-study-source-firebreathing.png : 1024 : 752 : 92 : False : 8 : 1206431 : root : nginx
image : webp-study-source-firebreathing.png.webp : 1024 : 752 : 92 : False : 8 : 71860 : root : nginx

------------------------------------------------------------------------------
Original or Existing Images:
------------------------------------------------------------------------------
| Avg width | Avg height | Avg quality | Avg size   | Total size (Bytes) | Total size (KB) |
| --------- | ---------- | ----------- | --------   | ------------------ | --------------- |
| 2238      | 1954       | 87          | 1295535    | 10364281           | 10121           |

------------------------------------------------------------------------------
Optimised WebP Images:
------------------------------------------------------------------------------
| Avg width | Avg height | Avg quality | Avg size   | Total size (Bytes) | Total size (KB) |
| --------- | ---------- | ----------- | --------   | ------------------ | --------------- |
| 2238      | 1954       | 92          | 393337     | 3146696            | 3073            |