WebP-Images without Plugin

WebP is a modern image format which shines through its fairly small filesize. Google recommends through their PageSpeed Insights that all PNG- and JPEG-files should be served as WebP images instead. There are of course plugins to facilitate the automatic generation of WebP versions of existing images but a few lines of PHP code and a .htaccess rewrite rule is really all that is needed to automatically serve all existing PNG- and JPEG- images as WebP versions to browsers that support it.

AdBlock is blocking the most important revenue source for this blog.

Introduction

Especially if you have a website that has been around for a while, you will have accumulated a lot of image files throughout the years. If your site is hosted on a dedicated server with shell access, you can use ImageMagick on the command line to turn all your existing images into WebP-versions. However, you’ll still need a .htaccess rewrite directive in order to actually serve those converted files to browsers that support the format. The approach I am taking is to direct all requests for PNG- and JPEG- files and redirect them to a PHP script that uses the PHP-extension Imagick to convert and serve WebP-files automatically. Most modern shared hosters have the PHP extension Imagick installed.

Automatically generate and serve WEBP-images with PHP

Automatically generate and serve WEBP-images with PHP

All examples shown in this article are tailored to a WordPress installation with all images of the website being in the default location “/wp-content/wuploads/*”. Make sure to adapt all file-paths provided in this article if you intend to use this method with a different CMS.

PHP Script

The PHP script itself is very straight-forward. In a standard WordPress installation the PHP script needs to be located at “/wp-content/uploads.webp.php” and will be called by a .htaccess rewrite rule that I’ll cover later. Whatever image file is passed to the script through the “file=” GET parameter will be converted to a WebP image version if possible. Since the script could technically be called from unauthorized, external users, it is absolutely mandatory to sanitize and validate the file path. If the passed file name is missing or invalid, the script will throw a “400 bad request” error. Assuming the path is valid and the file exists, an Imagick object is created, metadata is stripped, the compression quality set to 75 % and the resulting WebP image is sent to the browser. The image conversion function is neatly packaged inside a try-catch-block. In case something goes wrong, the script will return the appropriate “HTTP/1.1 500 Internal Server Error” response code.


<?php
// Sanitize and validate the file path $filePath = isset($_GET['file']) ? $_GET['file'] : null; if (!$filePath || !is_file($filePath)) { header('HTTP/1.1 400 Bad Request'); echo 'Invalid or missing file parameter.'; exit; } try { // Initialize Imagick $im = new Imagick(); $im->readImage($filePath); $im->stripImage(); // Remove unnecessary metadata // Set image format and compression $im->setImageFormat('webp'); // Use a constant for quality if desired $im->setImageCompressionQuality(75); // Output the image as a WebP format header('Content-Type: image/webp'); echo $im->getImagesBlob(); } catch (Exception $e) { // Handle errors gracefully header('HTTP/1.1 500 Internal Server Error'); echo 'Error processing image: ' . $e->getMessage(); } finally { // Clean up if (isset($im)) { $im->clear(); } } ?>

.htaccess Directives

The job of the .htaccess directives is to direct appropriate requests to the PHP script. Whether or not a request is appropriate for redirection is measured through a few conditions. The first rule ensures that the requesting browser (or other client) actually supports the WebP format. I’ll be honest and say that on my websites this condition is completely missing from the .htaccess files: It’s 2025, if your browser doesn’t support WebP, you’re SOL on my websites. Get with the times! The next condition is that the file either ends in .jpg, .jpeg or .png. Feel free to also add .gif or any other Imagick supported image format if you like. Imagick actually also handles animated GIFs quite well. Lastly, the conditions verify that the requested file matching all previous conditions is actually an existing file. If all those conditions are met, the script is internally rewriting the path to “/wp-content/uploads/webp.php?file=<filename>” where <filename> is of course the name of the image file requested originally.


RewriteBase /wp-content/uploads/

<IfModule mod_rewrite.c>
    # Check if the browser accepts WEBP
    RewriteCond %{HTTP_ACCEPT} image/webp

    # Check if the request is for an existing image file (jpg, png, jpeg)
    RewriteCond %{REQUEST_FILENAME} \.(jpe?g|png)$ [NC]
    RewriteCond %{REQUEST_FILENAME} -f

    # Redirect the request to the webp.php script for conversion
    RewriteRule ^(.*)\.(jpe?g|png)$ /wp-content/uploads/webp.php?file=$1.$2 [NC,L]
</IfModule>

Note that this is an internal redirect so the browser does not see a 301 redirect of any kind. Your old image file paths remain valid, they’ll only get served the WebP version instead.

Store WebP-Versions Permanently

So far the PHP script renders a WebP version of an image at runtime whenever the image is requested. If the same image is requested 10 times in a row, the PHP script will do the same job 10 times in a row. Even if you do have the necessary horsepower on your server, rendering an image on request will always take longer than simply serving an existing file without any processing. By adding just one line to the code (shown in bold), the PHP script can be directed to store the WebP image permanently alongside the original image. It’ll simply add “.webp” to the xisting file name. So if you have an image located in “/wp-content/uploads/2030/13/image.png”, a converted WebP version will be stored as “/wp-content/uploads/2030/13/image.png.webp”.


    echo $im->getImagesBlob();
    $im->writeImage($filePath.".webp");

By adding another .htaccess directive to run before the directive shown above, any request to an image that has previously been converted to a WebP image will be redirected to supporting browsers immediately, bypassing the PHP script completely.


<IfModule mod_rewrite.c>
    # Check if the browser accepts WEBP
    RewriteCond %{HTTP_ACCEPT} image/webp

    # Only apply the rule if the requested file is an existing jpg, png, or jpeg
    RewriteCond %{REQUEST_FILENAME} \.(jpe?g|png)$ [NC]
    RewriteCond %{REQUEST_FILENAME}.webp -f

    # Rewrite the request to the corresponding .webp file
    RewriteRule ^(.*)\.(jpe?g|png)$ $1.$2.webp [L,T=image/webp]
</IfModule>

You can see the script in action on this site. For instance, if you click in on the title image of this post you’ll notice that despite the file ending in .png, it is actually served as a WebP image. You might also notice that the image has a watermark in the bottom right corner. The watermark is also automatically inserted by my version of the PHP script.

The files can be downloaded from my WebP GitHub repository.

Please cite this article as:
Westerhold, S. (2025), "WebP-Images without Plugin". Baltic Lab High Frequency Projects Blog. ISSN (Online): 2751-8140., https://baltic-lab.com/2025/01/webp-images-without-plugin/, (accessed: January 14, 2025).

Sebastian Westerhold
Latest posts by Sebastian Westerhold (see all)

Leave a Reply

Your email address will not be published. Required fields are marked *