WordPress Site Speed Optimization Guide

A third of the websites on the internet use WordPress, and many of them have the same problem, they’re bloated and slow. This guide will show you how to fully optimize your WordPress site for speed.

WordPress is an excellent CMS but it has a tendency to become slow depending on the theme you choose, the quantity and type of plugins you use, and where you host it. That’s why I’ve spent years trying different themes and plugins, tweaking code, and testing different hosting providers in a quest to crack the code on how to make the fastest WordPress site possible. My efforts were accelerated after Google declared site speed as a ranking factor.

I’m happy to say that I’ve finally found the solution. In fact, you’re currently using the solution. This site is the fastest WordPress site I’ve ever created. Using Lighthouse – Google’s preferred and most modern performance measurement tool – I was able to achieve a First Contentful Paint (FCP), First Meaningful Paint (FMP), Speed Index, and Time to Interactive (TTI) of ~1 seconds on a simulated slow 4G network!

Lighthouse Report
Lighthouse performance report for the Coywolf website

I’ve written this guide to walk you through everything I did to achieve those amazing speed results. My hope is that you’ll be able to use this guide to optimize your own site for speed and further outrank your competitors.

  1. Choosing the right theme
    1. Barebones themes
  2. CSS optimization
    1. CSS frameworks
    2. Fonts
    3. Render blocking CSS
    4. CSS exclusions
  3. JavaScript optimization
    1. Load JS asynchronously
    2. Use Resource Hints
  4. Image optimization
    1. Best formats
      1. Format flowchart
    2. Size reduction
      1. Optimizer apps
    3. Lazy Load
    4. Serve WebP images
  5. Plugin management
  6. Adding Schema.org structured data
    1. WordPress functions for Article
    2. WordPress functions for BreadcrumbList
  7. Multifaceted caching and content delivery
    1. WPEngine configuration
    2. Cloudflare configuration
    3. WP Rocket configuration

How to choose the right WordPress theme for speed

Thanks to the popularity of WordPress, there are many pre-built themes that you could choose. If you take your time and carefully research and test out each theme, you will eventually find one that is optimized for SEO, uses modern coding standards, and is highly customizable.

I’ve personally had a good experience with Array Themes and was a paying customer for several years. Which was why I was pleasantly surprised when I heard WPEngine acquired them.

Even though there are good themes available like the ones created by Array, I have yet to find a non-barebones theme that is capable of achieving near AMP-level speed. The main reasons are because those themes prioritize functionality, ease-of-use, and customizability before speed. They meet those priorities by requiring code libraries and multiple plugins. That combination comes at the expense of being able to make the site as fast as it could be. That’s why I don’t recommend using non-barebones themes if you want your site to be as fast as possible.

Barebones Themes

A barebones theme – also known as a boilerplate or starter theme – is one that has all of the basic functionality of a typical WordPress site but without the standard plugin requirements, enhanced functionality, GUI-based customization, and pre-set design of a regular theme.

The most popular barebones theme is Underscores. It’s created and maintained by Automattic (the makers of WordPress) and is probably the most robust and flexible barebones theme available. So if you want to make a more complex WordPress site, I recommend using Underscores.

For me, Underscores is not barebones enough. It comes with predetermined includes and template parts structure, and it has more code than I want. That’s why I use the HTML 5 Blank theme instead.

The very first thing I do with the HTML 5 Blank theme is find and remove all references to CSS, JavaScript, and fonts. I also remove block level elements so that all that’s left are the WordPress functions to make the site work. Doing this provides me with a clean palette to work with and removes any chance of having my site load unnecessary code.

A stripped down barebones theme is the absolute best way to build a super fast WordPress site.

How to optimize CSS for speed

CSS tends to get out of control quickly. That’s especially true if a site’s design is complex, gets frequently updated, and is maintained by multiple people. Additionally, most sites have CSS files that load on every page that contains code for every possible layout on the site. They also typically use a CSS framework that includes thousands of lines of code that aren’t even utilized by the site.

CSS code bloat has a direct effect on how quickly a site can load and reach its FMP. To avoid CSS code bloat, I recommend approaching CSS in the following ways.

Avoid or reduce the use of CSS frameworks and libraries

A lot of sites use CSS frameworks like Foundation and Bootstrap because they make it easy to build out a site quickly. They are also designed to work with all major browsers and are compatible with previous versions. These frameworks have thought of just about everything, which is why they require so much code.

In an attempt to get around framework code bloat, they provide minimized versions. Some frameworks, like Foundation, also offer the option to pick and choose which components to download and use. While minimized files and custom framework downloads can help reduce the amount of code used, they still use a lot of code.

This is the point where you have to make an important decision. Do you trade site speed for ease-of-use or do you write your own simpler code and keep your site as fast as possible?

Consider this: If a site’s layout is so complicated that it requires a framework, perhaps the overall design should be reconsidered. A simpler design could reduce the amount of code needed and also improve its UX. Also, modern CSS properties like Grid enable sites to have flexible columns and rows with very little code, while maintaining compatibility with all major browsers.

Coywolf doesn’t use a framework and uses CSS Grid for columns and rows in the header and footer of the site. That’s one of the reasons this site is so fast.

Avoid or reduce the use of linked fonts

All modern browsers now support linking to and using different fonts. In fact, Google made it incredibly easy for web designers to utilize a diverse group of fonts thanks to Google Fonts. These fonts have opened up new style options for designers, but they’ve also slowed down page speeds.

If the font doesn’t exist on the visitor’s computer, the browser has to download it. In most cases, designers will use multiple fonts with multiple weights (thinness and boldness) along with their italicized versions. Font file sizes add up quickly. For example, if you use just one font like Lato, the download size quickly jumps to 1.2MB.

Linked fonts should be used sparingly or not at all. Instead of using linked fonts, use system fonts. System fonts are fonts that the OS uses throughout its GUI for displaying text. There are several benefits to using system fonts.

The following CSS code covers all major platforms and devices and is what Coywolf uses.

body { font-family:-apple-system, BlinkMacSystemFont,"Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell","Fira Sans", "Droid Sans", "Helvetica Neue",sans-serif; }

If you must use non-system fonts, consider using the font display descriptor for @font-face. That tells the browser to continue rendering the page and to display the text, even if it hasn’t downloaded the font yet. There are two font-display options to choose from, swap or optional. The swap option should be used if the font is critical for the UX. Otherwise, optional should be used, which tells the browser it isn’t crucial to the UX and the browser can make the decision to use it or not based on the network speed.


Include render blocking CSS on the page

The first thing a browser processes is the code on the page. If there’s code that’s required for the page to render correctly that’s not on the page, like a CSS file, the browser has to retrieve that file, which can slow down the rendering process. The best way to avoid that is to include all render blocking CSS on the page.

If you have minimal CSS for the entire site because you avoided using a framework, you can skip linking to a CSS file and include all of the CSS code on the page. I recommend keeping all of the CSS in a file and using a PHP include to insert the code into the HEAD area on the header.php file of the theme.

<style><?php include_once( 'app.css.php' ); ?></style>

Alternatively, if you still have too much CSS to put on the page, you will want to find and separate all render blocking CSS and put that on the page instead. You can then link to the file with the non-render blocking CSS and defer it by adding the following attributes and values to the link element.

<link rel="preload" href="path/to/stylesheet.css" as="style" onload="this.rel='stylesheet'">

Exclude CSS not used on a page

Another way to reduce the amount of CSS code that’s included when a page is visited is to exclude it. Coywolf uses this technique for the homepage and category pages.

The homepage and category pages use a CSS-based background gradient, and each one has its unique color. It takes several lines of code to make it compatible with all major browsers, and each one of those pages requires its block of CSS to make it work. I used PHP elseif statements to make it only include the code block needed to display the gradient background for the homepage or category page.

<?php if (is_front_page()) : ?> CSS only for homepage <?php elseif (is_category(1)) : ?> CSS only for category with ID 1 <?php elseif (is_category(2)) : ?> ... <?php endif; ?>

How to optimize JavaScript for speed

Some of the same optimizations I mentioned for CSSavoiding or reducing frameworks and libraries (e.g., JQuery), including render blocking code on the page, and excluding code that’s not used on the page – also apply to JavaScript (JS). For that reason, I won’t bore you by repeating them again in this section. However, there are a couple of things you should consider that are unique to JS.

Load JS asynchronously

For non-render breaking JS, you can add the async attribute to the link element to allow the browser to return and load it after it’s finished building the DOM.

<script async src="script.JS">

It’s important to note that browsers tend to load JS as early as possible even when you’re using async. That’s why Ben Morss, Developer Advocate at Google, recommends loading non-critical JS after the load event using a defer script. He suggests using the defer script created by Patrick Sexton.

An even easier way to identify and defer non-render blocking JS is to simply use the WP Rocket caching plugin. It comes with an option to automatically defer it.

Defer JavaScript
WP Rocket can automatically defer non-render blocking JavaScript

Use Resource Hints

If you have a page that you’re trying to funnel visitors to, like a shopping cart or checkout page, you can use the prefetch Resource Hint to have the browser load large JS files in the background. Then, when the visitor finally navigates to the page, it should load incredibly fast because the JS assets will already be stored in the browser’s cache.

<link rel="prefetch" href="script.js" as="script">

How to optimize images for speed

Images that are large and unoptimized can significantly slow down your site. Fortunately, there are several ways to optimize images for speed.

Use the best format for the image

The ideal image type to use on web pages is SVG. They are vector images that have small file sizes and look great on high definition screens. However, they should only be used for simple images like icons, logos and images that are not complex. For example, you wouldn’t want to use SVG for a photograph or any image with many colors and objects in it.

The next ideal image type after SVG is PNG. They are raster images that work well for simpler images, like screenshots. One of the best features of PNGs is that they support transparent backgrounds, but are typically much smaller in file size than a GIF.

The final image to consider using is JPEG. Like PNGs, they are raster images, but they typically maintain a smaller file size for complex images like photographs.

Image format examples
Coywolf Homepage ScreenshotJon drinking beer

I created this flowchart to help you pick the right image. Here’s a PDF version of it if you want a printer-friendly copy. Also, this flowchart was adapted from Daniel Box’s flow chart which was originally adapted from Ilya Grigorik’s flow chart.

Which image format should you choose?

Image Format Flowchart
Best image format flowchart

Reduce the size of PNGs and JPEGs

PNGs and JPEGs can be significantly reduced in size by changing their Quality and using an optimization tool. For example, PNGs are usually 24-bit, but you can use image editing software like Adobe Photoshop or Pixelmator Pro to export it as a much smaller 8-bit PNG. Similarly, JPEGs can be exported and made smaller by reducing their Quality percentage. For example, I was able to reduce the picture of a flower that I took from 360KB to just 78KB (over 78% smaller!) by adjusting the Quality percentage from 100% to 50%.

Export JPEG
Exporting and optimizing image in Pixelmator Pro

Image Optimizers

After reducing the file size of PNGs and JPEGs in an image editor, you can then use a free image optimizer to further reduce the file size, but without losing any visual quality to the image. I use Pixelmator Pro to edit and export images, and then use ImageOptim to reduce the file size just a little more.

ImageOptim Example
ImageOptim reducing the size of a PNG image

Lazy load images

Another way to speed up pages is to only deliver and display images that can be seen in the current browser view (above the fold). That method is called lazy loading and it forces the browser to only download images as the user scrolls down the page. Lazy loading is most helpful on long pages that have many images. It requires JS and you may need to alter the attributes in the IMG element to make it work correctly.

The best way to implement lazy loading is to use a single-purpose script (versus a library like jQuery) that supports responsive image attributes like SRCSET. Two popular scripts that do that are:

You can also use the built-in lazy load feature in the WP Rocket caching plugin. One thing to keep in mind is that lazy loading images can break the UX for jump links. That’s because when you click a jump link that takes you down the page, lazy loaded images haven’t been rendered and displayed yet. Coywolf uses WP Rocket for its caching but I had to turn off its lazy load feature because it kept breaking the jump link experience.

Serve WebP images

WebP image types are superior in quality and size compared to PNGs and JPEGs but they aren’t supported by all major browsers. That’s why I didn’t include the WebP format in the best format section. However, you can serve visitors WebP images without even making them.

Cloudflare supports replacing PNGs and JPEGs with WebP if it determines the WebP version is smaller than the original format and if the browser supports it. The feature is called Polish and it’s available to Pro accounts. Coywolf uses this feature to automatically convert and serve reduced size WebP images.

WebP Option
Serve WebP images using Cloudflare’s Polish feature

How plugins affect site speed

Every time you install and activate an additional plugin, it increases the chance of your site becoming unstable and slower. There are several reasons for this:

  1. Some plugins aren’t compatible with each other
  2. Plugins can add their own custom CSS and JS code that you can’t control
  3. Plugins can be dependent on code frameworks and libraries and will add links to them on all your pages
  4. Some plugin developers stop maintaining it, which can result in the plugin breaking in a future WordPress update

For those reasons, it’s important to use as few plugins as possible, and to only use plugins that you know are compatible, maintained on a regular basis, and don’t introduce a lot of new code that’s completely unnecessary.

Coywolf uses a total 5 plugins (6 if you count the Campaign Monitor add-on for Gravity Forms).

Coywolf only uses a handful of plugins that are essential for the site to function

I use the bare essentials of what I need to run the site and nothing else. Everything else gets added to the templates or the functions.php file in the theme. This is one of the main reasons the Coywolf site is so fast.

How to add Schema.org structured data to templates

In an effort to reduce the number of plugins I use on Coywolf, I decided to add Schema.org structured data directly to my theme’s templates. These are the four I added:

  1. Organization – Added to footer.php
  2. WebSite – Added to footer.php
  3. BreadcrumbList – Added to single
  4. Article – Added to single.php

Adding Organization and WebSite to the footer.php template makes the structured data appear in the source of every single page. Whereas, adding BreadcrumbList and Article to the single.php only includes that structured data on posts.

Since the structured data for posts contains dynamic data, I was able to use WordPress functions to insert the correct data. Here are all the functions I used to dynamically insert the post data into the structured data.

WordPress functions for dynamically inserting data into Article schema

"@id": "<?php the_permalink(); ?>"
"headline": "<?php the_title(); ?>"
"description": "<?php echo get_the_excerpt(); ?>"
"datePublished": "<?php the_time('c'); ?>"
"dateModified": "<?php the_modified_time('c'); ?>"
// For author type
"name": "<?php the_author(); ?>"
"url":"<?php echo get_the_author_meta('user_url'); ?>"
// For image type
"url": "<?php if ( has_post_thumbnail() ) { the_post_thumbnail_url( 'full' ); } ?>"

WordPress functions for dynamically inserting data into BreadcrumbList schema

// For category ListItem
"@id": "<?php echo $cat_link ?>"
"name": "<?php echo get_the_category( $id )[0]->name; ?>"
// For post ListItem
"@id": "<?php the_permalink(); ?>"
"name": "<?php the_title(); ?>"

Multifaceted caching and content delivery

A key component to Coywolf’s incredibly fast site speed is the use of multifaceted caching and content delivery services. These are the three services that make it all possible.

  1. WPEngine provides object caching and other caching features that are part of their proprietary EverCache system
  2. Cloudflare ensures that your site content is being delivered as fast as possible through its caching and speed optimizations
  3. WP Rocket is the most reliable, robust, and easy-to-use WordPress caching plugin available

WPEngine configuration

WPEngine is already well configured by default, but you will need to add SSL certificates to your site if you haven’t already in order to take advantage of HTTP/2 on Cloudflare. It’s easy to do because WPEngine provides free Let’s Encrypt certificates in the SSL section of your site’s admin area. Once you’ve configured your site to use SSL, you can then configure Cloudflare.

Cloudflare configuration

WPEngine has a CDN option but I prefer to use Cloudflare for my CDN because it has so many more options. The other nice thing about using Cloudflare is that WPEngine and WP Rocket are designed to work seamlessly with it.

WPEngine has a guide on how to set up your site on Cloudflare. The main things you will need to do on Cloudflare are:

  1. Create a CNAME record
  2. Create a full Universal SSL certificate

Once you complete those tasks, you can configure your site on Cloudflare with the same settings that Coywolf uses. These settings are organized by section. Keep in mind that some of these settings may only be available to Pro accounts.

WP Rocket configuration

I’ve made it very easy to add the same configuration that Coywolf has on WP Rocket to your own site. Simply download this configuration file and then import it in the Tools section of the WP Rocket admin on your own site.

WP Rocket Import
Import the WP Rocket configuration file

After you import the configuration file, go to the Add-ons section and turn on Cloudflare. Then click on Cloudflare located underneath the Add-ons side-nav option. Enter the API key, email, and Zone ID and save changes. Then turn on Optimal settings on the same page.

That’s it! You’re done. Well, are you ever really done?

Everyone’s site is different so performance will vary. It’s likely you won’t get your site to be as fast as Coywolf is but you might. The important thing is that if you implemented all or most of the items in this guide, you will have fully optimized your site for speed. It should be fast and you should be proud of yourself!