IAM

ARTICLE

Automatically Compressing Images in WordPress Without Costly Plugin

Compressing all PNG images across all Wordpress posts and pages might be cumbersome. However, plugins for bulk compression are usually not entirely free; they only allow to compress a limited number of images for free. Instead, downloading all PNG images, compressing them locally and adding a simple filter for the_content also gets the job done.

At the time of writing this article, this website contains roughly 1.8k images totalling more than 400MB. Especially in the early phase of this webpage, I did not compress all images — yes, as a web developer, I should have known better. For quite some posts and pages, however, I explicitly wanted to avoid lossy compression for showing research results in image processing or computer vision.

After five years of running this page, I found it is finally necessary to compress most of the left-over PNG images — for faster loading and search engine optimization. However, plugins such as Compress JPEG & PNG Images, Smush Image Compression and Optimization or ShortPixel Image Optimiser only offer very limited free contingents for image compression — and images might be transferred to third party servers. In the end, I decided to look for an alternative solution.

Download and Compress Images

First, I used a standard FTP software such as FileZilla to manually download all images. The easiest way is to download the full wp-content/uploads directory. Alternatively, the media directories within wp-content/uploads, usually named according to years and months (2018/01, 2018/02 etc.) can be downloaded individually.

Second, the images can be compressed locally. There are many options to do this; on Linux, for example, mogrify (part of ImageMagick) can be used:

# Install mogrify:
sudo apt-get install imagemagick
# Optionally resize images first:
find . -name '*.png' -execdir mogrify -resize 50% {} \;
# Convert images:
find . -name '*.png' -execdir mogrify -format jpg -quality 95 {} \;

On Windows, some explorers such as Directory Opus offer easy bulk compression. Alternatively, ImageMagick is, of course, also available for Windows.

Upload Images and Update Posts and Pages

After compression, the JPG images can be uploaded again. Here, choosing not to overwrite any images makes sure that nothing changes. This is mainly because the posts and pages have not changed yet — they are still referring to the original PNG files. So, third, all posts and pages need to be updated. A simple filter for the_content will take care of this automatically. This has another advantage: if some PNG files have not been compressed, the posts and pages will remain unchanged. The filter is added to the theme's functions.php file. For simplicity, the filter assumes that the full path is wp-content/themes/your-theme/functions.php (and wp-content/uploads holds the media directories):

add_filter('the_content', 'iamdavidstutz_png_to_jpg');

/**
 * Replacing pngs with jpgs.
 */
function iamdavidstutz_png_to_jpg($content) {

    $matches = NULL;
    // THE REGEX NEEDS TO BE ADAPTED TO THE CORRECT DOMAIN AND INSTALLATION DIRECTORY!
    $regex = '#src="(http|https)://davidstutz.de/wordpress/(wp-content/uploads/[0-9]+/[0-9]+/[^.]+\.png)"#';
    preg_match_all($regex, $content, $matches, PREG_SET_ORDER);

    foreach ($matches as $match) {
        $png_file = $match[2];
        $jpg_file = substr($png_file, 0, strlen($png_file) - 3) . 'jpg';
        
        if (file_exists(__DIR__ . '/../../../' . $jpg_file)) {
            $content = str_replace($png_file, $jpg_file, $content);
        }
    }

    return $content;
}

The code can also be found on GitHub: davidstutz/wordpress-iamdavidstutz. Note that the filter needs to be adapted to the correct domain and directory structure; updating the regular expression

// 1. Update the domain (can also be found in the WordPress settings).
// 2. Update the directory structure, if wordpress is not installed in the 'wordpress' subdirectory.
$regex = '#src="(http|https)://davidstutz.de/wordpress/(wp-content/uploads/[0-9]+/[0-9]+/[^.]+\.png)"#';

should be sufficient. If the assumed directory structure (see above) is wrong, it might also be necessary to change the relative path from functions.php to the media directories:

// Assumes that functions.php lies in wp-content/theme/your-theme/ and the media directory in wp-content/uploads:
file_exists(__DIR__ . '/../../../' . $jpg_file)

Conclusion

Compressing old PNG images in a WordPress installation is easily possible without relying on costly plugins, as described above. However, this assumes that images uploaded in the future are either compressed automatically (using some plugin), or compressed beforehand. On this page, this approach worked very well for more than 1.8k images amounting to roughly 400MB — not accounting for programming time, downloading, compressing and uploading was done in less than 30 minutes.

What is your opinion on this article? Let me know your thoughts on Twitter @davidstutz92 or LinkedIn in/davidstutz92.