Optimize the size of .PNG images automatically on your webserver with optipng

Mattias Geniar, Sunday, March 27, 2016 - last modified: Tuesday, January 31, 2017

A couple of weeks ago I write a blogpost on the technical aspects of SEO. One of the readers translated it into French and reported an additional tip: my images in that post were way too big.

François was even kind enough to include a screenshot with the savings I missed out on.


I used to have a workflow where I ran every image through ImageOptim to compress them as much as possible. But it's a manual step that's too easy to ignore.

So here's a way to automate it. These couple of scripts automatically run and compress your files for you, so you don't have to think about them ever again. They use the optipng tool in the background.

Why even do this?

Now, normally, optimizing images is part of the build or release process. Before your images even hit your servers, they're in its most optimal form. However, reality isn't always that easy. If you run a dynamic site like WordPress or Drupal, anyone can upload images and they don't necessarily have the same requirements for images like you do.

This technique catches those uploads, encodes them in a better format and nobody notices.

Nobody but you: your server uses less bandwidth and consumes less disk space. This technique also processes asynchronously: there is no delay in the uploads, images are processing a while after they're uploaded.

Install optipng

You start by installing the optipng tool.

On Red Hat or CentOS:

$ yum install optipng

On Ubuntu / Debian:

$ apt-get install optipng

Now, let's automate this.

Automatically optimise your new images every hour

Friendly reminder: before running any of these tools, make sure you have a back-up to restore from!

Update: just to very clear, this only optimises new images. It searches for them, every hour, and processes them. It does not keep processing the same images over and over. The search hardly demands any server resources.

The optipng tool itself works pretty simple. The most basic command is optipng image.png, which optimises the image called 'image.png' and replaces it on your server (so you lose the original!).

We can add some additional parameters that further reduce the size, increase the compression ratio and strip all META info from the PNG file you don't need. Here's my current command for the most optimal size.

$ optipng -o7 -f4 -strip all -quiet image.png

This takes the file called 'image.png', runs all sorts of optimisations over it, strips meta data and overwrites the file with the new and improved version.

Now, to run this automatically on your server, all you need is the following cronjob. Let this run every hour, and it'll take all PNG files created in the last hour and optimises them for you.

$ find htdocs/wp-content/ -mmin -60 -name '*.png' -print0 | xargs -0 optipng -o7 -f4 -strip all -quiet -preserve

This essentially does these things:

  1. Find all *.png files in the 'htdocs/wp-content' directory that were created in the last hour (this is the directory where WordPress uploads its content)
  2. For each of those found files, run the optipng command on them

Add that to your crontab and you never have to think about it again.

$ crontab -l
0 * * * * find ~/htdocs/wp-content/ -mmin -60 -name '*.png' -print0 | xargs -0 optipng -o7 -f4 -strip all -quiet -preserve

All done!

Optimise all your existing images

If you're only thinking about activating this after you have a bunch of files, you may want to run this tool over all your images -- not just the ones modified in the last hour.

I'll repeat myself, but here it goes: before you run this over all your files, have a back-up! These settings replace the original file, so you no longer have the original.

$ find ~/htdocs/wp-content/ -name '*.png' -print0 | xargs -0 optipng -o7 -f4 -strip all -quiet -preserve

That command searches for all '*.png' files in the ~/htdocs/wp-content/ folder and runs the optipng command over all of them.

Hi! My name is Mattias Geniar. I'm a Support Manager at Nucleus Hosting in Belgium, a general web geek & public speaker. Currently working on DNS Spy & Oh Dear!. Follow me on Twitter as @mattiasgeniar.

Share this post

Did you like this post? Will you help me share it on social media? Thanks!


Noelinho Monday, March 28, 2016 at 13:38 - Reply

I like this approach, but there may be one further way to streamline the process (and to eliminate the need to use crontab). Is there some find of WordPress hook that can be used at the point of upload to process the images, so it’s done immediately?

    Mattias Geniar Monday, March 28, 2016 at 18:55 - Reply

    A quick search shows a couple of WordPress plugins that do similar things. However, what I like about this approach is the asynchronous aspect of it: it works regardless of CMS or language (PHP, NodeJS, Ruby, …) and can be implemented by a sysadmin without knowing the code or CMS of the site.

    I’m sure there are more elegant ways, but it’s a really quick 2-minute hack with immediate results, for any site. :-)

Jukka Heino Tuesday, March 29, 2016 at 10:39 - Reply

I think your approach to optimizing only new images will not work correctly, because when optipng rewrites the image files it also changes the file’s ctime so that it will processed again the next time the cronjob runs. Using mtime instead of ctime and passing -preserve to optipng should work as intended.

I would also recommend using the -print0 option of find and the -0 option of xargs to prevent filenames with whitespace from breaking the cronjob.

nothrem Friday, April 1, 2016 at 12:27 - Reply

I use apngasm/apngopt which is an alternative tool. But what I have noticed is that sometimes the optimized image is a lot larger than the original, especially when the image is a small transparent icon. Then I have to manually or by script check which image is smallest and use it.
Have you verified that this tool always create smaller image when you overwrite the original?

Leave a Reply

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