HOW TO: Set up Webp image support with Webp Express on Nginx (CDN-compatible/no .htaccess)

May 11, 2022

The WordPress Webp Express plugin (by Bjørn Rosell) is an incredible tool to both generate and serve .webp format images on your WordPress site and support webp. It automates both the conversion of .jpg and .png format images, and it offers multiple methods of implementing the logic to only serve .webp images to browsers that support it (ie, Chrome does, Safari does not at the time of writing).

Supporting .webp is a great way to pass Google Pagespeed Insights and Lighthouse's Serve images in next-gen formats metric.

While it does lots of fancy stuff out-of-the-box, there are quite a few options in the plugin settings that may be a bit daunting at first - especially if you're not on an Apache server.

In this article I will show the best configuration for Nginx (and Cloudflare if you happen to be using that too). This method specifically does not rely on the use of .htaccess so it will work with PHPFPM.

Step 1: Install the plugin & add use this...

Add this to your nginx configation:

# WebP Express rules
    location ~* ^/wp-content/.*\.(png|jpe?g|svg)$ {
        add_header Vary Accept;
        expires 365d;
    }
    location ~* ^/wp-content/.*\.webp$ {
        expires 365d;
        if ($whattodo = AB) {
            add_header Vary Accept;
        }
    }
    if ($http_accept ~* "webp"){
        set $whattodo A;
    }
    if (-f $request_filename.webp) {
        set $whattodo  "${whattodo}B";
    }
    if ($whattodo = AB) {
        rewrite ^(.*) $1.webp last;
    }
    if ($whattodo = A) {
        rewrite ^/wp-content/.*\.(jpe?g|png)$ /wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content break;
    }
# (WebP Express rules ends here)

If you want to play around with this nginx configuration, you can read more about the logic and different methods from the plugin developer here. If you set your browser expiry settings in nginx, pay special attention to the # Optionally specify browser cache expiry line. The first part of this rule excludes webp files from any existing nginx expiry header rules, so the # Optionally specify browser cache expiry block section handles that later on in the rule.

Change the 365d value as necessary.

Step 2: Configure the plugin settings

There are an absolute ton of ways to configure this plugin, but I'll save you some trouble if you want to get up and running nice and fast. Here are the settings I use for webp when configuring for an Nginx server behind Cloudflare:

  • Operation mode: Varied Image Responses
    • Scope: All content
    • Image types to convert: Both jpegs and pngs
    • Destination folder: Mingled
    • File extension: Append ".webp"
    • Destination structure: Document root
    • Cache-Control header: Do not set
  • Create webp files upon request? Unchecked
  • Enable redirection to converter? Unchecked
  • Create webp files upon request? Unchecked
  • Jpeg Options
    • WebP encoding: Auto
    • Quality for lossy: Fixed quality, 100
    • Quality for lossless: 100% lossless
  • PNG options
    • Webp encoding: Lossless
    • Quality for lossless: 100% lossless
    • Metadata: No metadata in webp
    • Convert on upload: Checked
  • Alter HTML? Unchecked

It's worth noting that while these are the settings I use to ensure compatibility with Cloudflare, they should work fine if you're using another CDN, or no CDN at all.

Conclusion: Fully support webp on Nginx

With this configuration, any browser request for a .jpg or .png will automatically try to load the .webp equivalent only in browsers that support that format. If the .webp equivalent doesn't exist - the original image will be served to the browser but also sent to the plugin's converter so it will be ready for the next request.

If you want to force all of your existing images to be converted manually, just click the Bulk Convert button on the plugin's settings page. Keep in mind, the batch conversion can take a good deal of time depending on how many images you have.

This is the best method I've used for supporting webp on Nginx (and it's free). This should take care of the Serve images in next-gen format flag in Google Pagespeed Insights.