Nginx rule to serve WebP images when available

nginxrewrite

I am trying to server webp images to supporting browsers by using the code
below. I have already .webp images in addition to .png and .jpg images of the same file.

For example if have an image named vacation.jpg, i also have vacation.jpg.webp.

The code below works but it was overwriting my other location blocks before i switched to a CDN. Now i am using CDN that code block has no effect, no WEBP images in response and also not overwriting my other location blocks.

Here is how to do it:

Add to the /etc/nginx/mime.types

“image/webp webp”

Add to the main nginx.conf file:

map $http_accept $webp_suffix {
  default “”;
  “~*webp” “.webp”;
}

Add to the server block:

location ~* ^/wp-content/.+\.(png|jpg)$ {
  add_header Vary Accept;
  try_files $uri$webp_suffix $uri =404;
}

Restart or reload nginx.

Example of my other location blocks:

Disable Hot-Linking

location ~ .(ico|gif|png|jpe?g|svg|webp)$ {
    valid_referers none blocked example.com *.example.com;
    if ($invalid_referer) {
       return 403;
   }
}

Allow Cross-Origin For Static Domain Files

    location ~ \.(ico|gif|png|jpe?|svg|webp|eot|otf|ttf|ttc|woff|woff2|ogg|js|css|font.css)$ {
        add_header Access-Control-Allow-Origin "*";
        #gzip_vary on;
        expires 30d;
}

Best Answer

The original question was changed completely after I posted this answer and this answer is no longer relevant to the question.


Even though your intention is good, the fact that you do a 302 redirect when a .webp version is found, makes the gain smaller.

The redirect adds two round-trips in latency to picture loading time, and if the picture is small enough, it will take longer to receive the .webp version than the original.

To fix this, you should generate the URLs in your website code directly.

However, to answer your current question, you can use this set of rules:

location ~ ^(/wp-content.+)\.(jpe?g|png)$ {
    set $red Z;

    if ($http_accept ~* "webp") {
        set $red A;
    }

    if (-f $request_filename.webp) {
        set $red "${red}B";
    }

    if ($red = "AB") {
        add_header Vary Accept;
        return 302 $1.webp;
    }
}

The idea here is that one has already captured the first part of the filename in your location directive. Therefore the first part of the new location is in the $1 regex capture variable. So, one simply uses the $1 in the return directive. I use return here, since it is faster than rewrite.