phpwordpressdev
(updated ) ~4 min (742 words)

WordPress, Nginx, Apache and webp images

Photo showing the WordPress Plugin overview

So you want to use webp with WordPress but not install just another (paid) plugin? With the help of some bash, ImageMagick and nginx or apache you're ready to go.


Why not installing another plugin?

Feel free to buy one of the many image optimization and converting plugins out there! Many of them are good and convenient.

I wanted to keep the plugin count down and not pay for something that's actually not that hard to achieve.

So I thought a bit about the problem, wrote a little bash script and did some research about the best ways to use the images in WordPress.

Ok, now show me the script, please!

Hold your horses! This is actually a very nice task which can be done quite easily if you have access to the webserver. First, the requirements:

  1. You need access to your webserver via ssh (it's a bash script after all)
  2. You need ImageMagick installed (we need convert)
  3. You need to be able to create cronjobs (crontab -e)
  4. For nginx, you need access to the webserver's config

All set? Let's continue then...

The script convert-webp.bash:

#!/bin/bash

if [ -z "$1" ]; then
  echo "missing base target path parameter"
  exit 1
fi

BASE=$1

find "$BASE"/* -type f \( -iname \*.jpg -o -iname \*.png -o -iname \*.jpeg \) | while IFS= read -r file; do
  # if the conversion target already exists, we continue
  if [ -f "$file.webp" ]; then
     continue;
  fi

  # make sure we do not convert accidentally included webp files, again
  if [[ $file = *".webp" ]]; then
    echo "ignoring webp files"
    continue;
  fi

  # now doing the conversion, we could also tune the quality a bit here
  convert "$file" -quality 50 -define webp:lossless=false "$file".webp
  echo wrote "$file.webp"
done

Now make the script executable: chmod +x convert-webp.bash. The script can be called via ./convert-webp.bash path/to/your/wp-content/uploads.

To create a cronjob: crontab -e and set:

*/30 * * * * bash /path/to/script/convert-webp.sh /path/to/your/wp-content/uploads

So the first part is done!

Configure nginx or apache

Normally using webp if existing can be done with all webservers. Nginx can do a lot of things especially for static files or for caching and that's why I chose it here.

Nginx config changes

I did this first with nginx since I created my own webserver and added the following: First, in the http level or the root level for a vhost definition, configure the webp suffix:

map $http_accept $webp_suffix {
    default        "";
    "~image/webp"  ".webp";
}

In the server level add a location (or change the existing) for images:

location ~* \.(jpg|jpeg|gif|png|webp)$ {
    add_header Vary Accept;
    add_header Cache-Control "public, immutable";
    try_files $uri$webp_suffix $uri =404;

    expires 1y;   
}

This tells the webserver to always try using the webp_suffixed file if it exists. Otherwise, it would use the original or give a 404 error if nothing found.

Also, using a long cache time is recommended for assets, so we're going with 1 year here.

Apache .htaccess changes

Ok, I haven't tested this yet, but in theory it should look something like this:

RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} (.*)\.(png|gif|jpe?g)$
RewriteCond %{REQUEST_FILENAME}\.webp -f
RewriteRule ^ %{REQUEST_FILENAME}\.webp [L,T=image/webp]

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

This should do the same as the nginx config from above:

  1. Check if the webp file exists and use it as a replacement for any other image type.
  2. Add the Vary Accept header



And now have fun!

Deleting webp files for deleted images

Since you might delete some images we have to delete the generated webp files from time to time as well. So try the following script:

#!/bin/bash

if [ -z "$1" ]; then
  echo "missing base target path parameter"
  exit 1
fi

BASE=$1

find "$BASE"/* -type f \( -iname \*.webp \) | while IFS= read -r file; do
  ORIGINAL_FILE="${file%.*}"

  # if the original file still exists we continue
  if [ -f "$ORIGINAL_FILE" ]; then
     echo "skipping $file because original still exists"
     continue;
  fi

  # if the original doesn't exist anymore we delete the .webp
  echo "now removing webp file $file"
  rm -f "$file"
done



Image Attribution
Main image WordPress Plugins Stephen Phillips - Hostreviews.co.uk auf Unsplash