One of the most overlooked aspects of WordPress assets optimization actually has nothing to do with WordPress per-se; It’s all about how we deliver static assets to our visitors’ browsers.

What are WordPress assets?

We use the term assets to describe all static files that your web server sends to visitors and fall into three types: javascripts, stylesheets and images. The former two are usually enqueued in our plugins and themes while the latter are uploaded through the media library.

But first, a word on cache busting

When we visit a website, the server first sends back HTML that WordPress generates, followed by all the assets, declared in that HTML, usually starting with CSS file(s), then images that either appear in the content or are pulled in through CSS and if your theme has been developed properly, javascript files at the end, since they are the last to be included (enqueued) in the source.

This means on average that for every page load you see on screen, your browser needs to make anywhere between 10 and 100 requests, every one of which is a roundtrip from your browser to the server, and back. So on an unoptimized web server, if you refresh the page, the whole process takes place all over again.

Apart from the fact that each request adds up to the total time it takes for the page to be fully rendered on your screen, this also has an impact on the server, since it has to process all those requests over and over again.

In order to avoid that, web servers allow administrators to set what we call an expires header. This is a small bit of information that tells the browser for how long a particular file will remain unchanged. So if we set it to +2 days, it means the browser will save the file into it’s cache and when you refresh the page, it’ll pull that file in, instead of contacting the server - until the expires time has passed.

Now imagine that you release a new theme version which has a completely new look but your new CSS file is named exactly the same as the previous one (let’s say style.css). After you deploy it, a returning visitor comes to your site and their browser just returns the old CSS, making your website look broken.

In order to remedy this we use a technique, called cache busting. Despite it’s fancy term it’s a really simple approach of solving the problem of outdated assets by:

  • renaming the new file (style.css -> style.2.css)
  • appending a query string (style.css -> style.css?ver=2)

By doing one of these we tell the browser it’s a new file so it makes a new request to the server instead of using a cached one. WordPress does the latter, but that presents a problem to some content delivery networks (CDNs, more on those later).

There is absolutely no reason to set expiry headers to just two days, once cache busting is configured, since we assume it will never change. Some administrators then set those to a year or even max, which sets it to something like year 2037. I’ll be 53 then :)

Optimizing stylesheets and javascripts

Stylesheets (.css files) and javascript (.js files) are essentially plain text files which means the optimizing process/logic is pretty simple (and same for both, so I’m only going to give an example on css); If you have multiple stylesheets, combine all the files into one, then remove all the spaces and newlines so what you get is one single file with one single but really long line.

/* Before */
h1 {
  font-size: 18px;
p {
  font-size: 13px;

/* After (perfectly valid CSS) */

I know it doesn’t look much, but just by doing this we minified the file from 51 to 38 bytes, effectively reducing it’s size by 25%! And depending on the size and complexity of your css, you can get up to 40% savings or more. Plus, if you merged files, you saved your visitors a couple of requests. Faster website ahoy!

Of course, doing this manually isn’t the way to go, so there are two solutions:

  • use a task manager like Grunt or Gulp
  • use a plugin

Even if you use a task manager (I've written a gist for Gulp workflow) to build your minified css for your theme, chances are you’re using some plugins that include unminified assets, which means the only way to go is using a plugin.

Warning: There’s a high chance that turning on any optimization plugin breaks your site, so try it in a staging environment first!

If you followed my previous tutorial with WP-Rocket, then turning on optimization is really easy, just visit settings and check these two checkboxes:

WordPress static file optimization WP-Rocket
WP Rocket minification settings

If not, then most of other caching plugins (such as W3 Total Cache) also support this out of the box, but I’m not personally a big fan of it, so I recommend two other plugins to do the job: Minit and Minit-YUI. They are not in the official repository and require your server to have Java installed, but very well the effort. SSH on the server, then enter the following command (make sure you enter the correct path to your plugins directory!):

$ sudo apt-get -y install git openjdk-6-jre
$ cd ~/www/
$ git clone
$ git clone

Now log in to WordPress, activate both plugins and watch the magic happen!

Unfortunately, Minit comes without any kind of settings screen, so if you need to exclude some files from minification, like conditional Internet Explorer stylesheets, then you’ll have to add those to your functions.php, like so (an example from twentyfifteen theme):

function exclude_stylesheets($stylesheets) {
  return array( 'twentyfifteen-ie', 'twentyfifteen-ie7' );
add_filter( 'minit-exclude-css', 'exclude_stylesheets' );

Optimizing images

Images are not saved as plain text files but in binary code, which means you can’t take the same approach to their optimization. As with stylesheets, you could do it manually with an image processing sofware there are plenty of (free and premium) plugins that do that for you. I recommend EWWW Image Optimizer. Once you install it, chances are you’ll see some permission errors, like these:

WordPress static file optimization ewww
EWWW Image optimizer errors

To solve them, SSH on the server, run the following commands (again, make sure the directory is correct):

$ cd /home/webmaster/www/
$ mkdir ewww
$ sudo chown :www-data ewww
$ sudo chmod 775 ewww

The plugin will automatically copy the necessary files it needs to process uploaded files. Once you try it out, you’ll notice some files’ sizes get reduced by 30% or more!

There’s also one more plugin I’d like to mention and that’s Kraken. It’s a plugin that remotely optimizes your images so all the processing takes place on their servers. Their plans start with $5/month and it’s up to you to decide whether you want (or need) to offload this task.

One GZIP to rule them all

While serving optimized assets helps reducing the number of requests made to the server and faster file transfers (due to their smaller size) there is one more step to take: file compression.

All modern browsers support compressed files and the difference can be quite significant. To get it working, all you need to do is add the following config to your Nginx file:

gzip on;
gzip_buffers 16 8k;
gzip_comp_level 9;
gzip_http_version 1.0;
gzip_min_length 0;
gzip_types text/plain text/css image/x-icon image/svg+xml image/png image/jpg image/jpeg text/js text/php application/javascript application/x-javascript;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";

Or, if you’re using Apache, use the snippet recommended by the Codex and put it into .htaccess.

You probably won’t see any significant improvement with images, but stylesheets and javascripts sometimes get compressed by 90%. Yes, that’s ninety percent, here’s how to tell:

WordPress static file optimization GZIP explained
The difference between compressed and uncompressed file size.

What about CDN?

When it comes to WordPress speed optimization, there are numerous articles online suggesting that you use a content delivery network. I would argue it’s actually the last step you should take when optimizing your assets.

The reason being that you first need to discover where most of your relevant visitors come from; If they are already geographically near your server (near being a relative term, 1000km is a reasonable mark) then using CDN is meaningless.

On the other hand, if they are distributed all over the world, then it makes perfect sense.

So how do they work?

When you register your account with a CDN, you usually receive a subdomain which you then prepend your static files with, so they are instead loaded from them ( becomes something like

Because on the very first request the files on the CDN don’t exist yet, the so-called pull-zone comes in effect (and you have to configure it). The pull-zone is essentially the original source of those files, your WordPress site URL.

After the first request is made, the CDN pulls the files from your WordPress, serves it to the visitor and then distributes it to other servers in it’s network so that on all subsequent requests those files (with really long expiry dates) are served instead the original one.

I know, a bit complicated - this video explains it better:


It really takes a little effort that goes a long way when it comes to static files you need to serve, and like a good site owner, everyone should do it, regardless of how many visitors you have or how big your site is.

If you have any other tips, feel free to leave a comment below.

This also concludes our series of tutorials on speed optimization, so for next week I’m preparing a tutorial on security, the second topic our subscribers requested the most.