Nginx WebP Image Serving

Conditionally serve WebP images with Nginx — up to 80% smaller than JPG/PNG with automatic fallback for unsupported browsers.

Originally published April 2017

On This Page

Introduction

This page demonstrates how JPG/PNG images converted to WebP format via the optimise-images.sh batch image converter tool can be served conditionally via Nginx web server to web browsers that support WebP image formats.

See the community forum thread for WebP conversion details. The optimise-images.sh tool also has an optimise-webp-nginx mode which allows the Nginx config syntax below to be auto-generated based on your web directory path containing your JPG/PNG and WebP images, and can also auto-generate a static HTML gallery comparing optimised resized JPG/PNG images side by side with their WebP converted counterparts.

WebP Format Benefits

WebP format images are usually much smaller in size than JPG/PNG files (up to 80% smaller), allowing web pages to load faster. WebP is supported by most modern web browsers including Google Chrome, Opera, Firefox, Edge, and Safari. For older browsers that do not support WebP, the configuration below provides automatic fallback to standard JPG or PNG images.

The guide below is for Nginx users who are not using the Nginx ngx_pagespeed module, which has filters that allow on-the-fly auto conversion of JPG, PNG, and GIF files to WebP format for browsers that support it. You can read up on the benefits of the ngx_pagespeed module here.

Nginx WebP Configuration

Step 1. Include the WebP Config

In order for Nginx to conditionally serve WebP format images to only web browsers that support it, set up a conditional Nginx map which inspects the client’s web browser Accept header for the WebP mime type. Centmin Mod 123.09beta01 and newer versions already have the /usr/local/nginx/conf/webp.conf include file set up in /usr/local/nginx/conf/nginx.conf. If you are using an older version, add an include within the http{} server context:

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

Step 2. Create the WebP Map Configuration

Within /usr/local/nginx/conf/webp.conf add the following contents (updated April 2, 2020 for Cloudflare compatibility):

/usr/local/nginx/conf/webp.conf
map $http_accept $webpok {
   default   0;
   "~*webp"  1;
}

map $http_cf_cache_status $iscf {
   default   1;
   ""        0;
}

map $webpok$iscf $webp_extension {
  11          "";
  10          ".webp";
  01          "";
  00          "";
}

Cloudflare Compatibility

The updated version allows proper compatibility with Cloudflare Proxy and DNS-only modes. When Cloudflare Proxy CDN mode is detected, Centmin Mod Nginx WebP is disabled because Cloudflare’s cache does not support the HTTP Vary header. When Cloudflare DNS-only mode is detected (or in the absence of Cloudflare), Nginx WebP is enabled.

The logic works as follows:

  • When the browser supports WebP (HTTP Accept header contains image/webp), $webpok is set to 1
  • When the cf-cache-status header exists (Cloudflare Proxy mode), $iscf is set to 1; if absent, it is set to 0
  • When $webpok$iscf = 10 (browser supports WebP and no Cloudflare proxy), $webp_extension is set to .webp — enabling Nginx WebP support by appending the extension to the original image filename

Step 3. Vhost Location Block

Within your Nginx vhost’s location context (for example, /webp), add the following. Remove the X-Robots-Tag header if you want the directory to be indexed by search engines, and remove autoindex if you do not want directory contents to be viewable. For Nginx ngx_pagespeed users, uncomment the pagespeed off (for ngx_pagespeed 1.12.34.3-stable and below) or pagespeed unplugged (for 1.13.35.1-beta and above) to disable ngx_pagespeed within this directory.

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

The Cache-Control header’s no-transform directive ensures that no transformations or conversions are made to the resource. As MDN explains, this prevents proxies from converting between image formats to save cache space or reduce traffic on slow links.

WordPress Uploads Example

This example is for WordPress’s /wp-content/uploads/ directory. Notice the differences in the inner location context match for .png and .jpg/.jpeg images. You will need to adjust this for your specific web application. The autoindex on directive is commented out/disabled since you probably do not want images in the uploads directory to be directly visible and listed.

WordPress vhost config
# webp extension support if you are converting /uploads images to webp
location ~ ^/wp-content/uploads/ {
  #pagespeed off;
  #pagespeed unplugged;
  #autoindex on;
  #add_header X-Robots-Tag "noindex, nofollow";
  location ~* ^/wp-content/uploads/(.+/)?(.+)\.(png|jpe?g)$ {
   expires 30d;
   add_header Vary "Accept";
   add_header Cache-Control "public, no-transform";
   try_files $uri$webp_extension $uri =404;
  }
}

Step 4. Restart Nginx

Restart Nginx for changes to take effect:

service nginx restart

Demo WebP Images

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

Original bees.png 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 showing bees.png image used for conditional WebP serving test
Demo bees.png image — served as WebP to supporting browsers

Browser DevTools Verification

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

Chrome/Opera DevTools showing bees.png served with WebP mime type, confirming conditional WebP serving is working
Chrome/Opera DevTools — WebP mime type confirmed

While unsupported browsers (older versions of Firefox, IE11/Edge, Safari) will see the PNG version (Mime Type = PNG):

Firefox DevTools showing bees.png served with PNG mime type, demonstrating automatic fallback for non-WebP browsers
Firefox DevTools — PNG mime type fallback

Explore Full Nginx Documentation

Learn more about Nginx configuration, SSL/TLS setup, vhost management, and performance tuning in Centmin Mod:

View Nginx Documentation