Troubleshoot and Speed Up WordPress

Speed up WordPress page load tutorial

Learn how to analyse your WordPress pages and make them load faster. We’ll cover some easy-to-follow and repeatable techniques for improving page-speed. Useful for designers and developers.

A slow WordPress page speed is usually caused by a combination of factors. This means there’s no single, easy solution to speed up your site. But, there is a process we can follow that will always help:

  1. Analyse the problem
  2. Implement a change
  3. Measure the effect
  4. Keep repeating until the speed is good
Google Lighthouse page speed score
Google Page Speed test

Analyse the page-load

Important: The first thing we need to do is stop guessing why the page is slow to load. We need to know why the page is slow.

Lets start by taking your browser, computer and Internet connection out of the equation by running our tests server-to-server. We’ll use GTmetrix to analyse the page-load because it gives us an easy-to-use network request/response waterfall. If you don’t already have a free account with GTmetrix, grab one now and go to your Dashboard. Expand the “Analysis Options” button and pick a test location that best represents your site’s prospective audience. Most of my clients are in the UK, so I use London as my test location.

GTmetrix server test location
GTmetrix server test location

Enter the URL you want to test, then hit Analyze. The test won’t take long – usually less than a minute.

When the the analysis has finished, you’ll get an overall grade and some numeric metrics. For now, we can ignore the grade and the metrics because we want the waterfall. Here’s an example from one of my tutorial pages:

Example network request/response waterfall from GTmetrix
A GTmetrix network request/response waterfall

The chart reads from top-to-bottom and left-to-right. The top line represents the first request in the page-load sequence, when the browser asks the server for the page’s HTML. This needs to be very quick, as it underpins the rest of the page-load. We’ve got 84ms here, which is good.

If you’re initial HTML-fetch is longer than 200ms then you’ve got a problem:

  1. Double-check that you’ve set the server test location correctly in GTmetrix. If your website is hosted in London but you’re testing from Vancouver, you’ll see a delay because of you’re on the wrong side of the planet.
  2. Check your WordPress hosting is not overloaded/cheap/rubbish. Some of the big companies that say they’re “really fast for WordPress”… are actually lying. Review the different types of WordPress hosting available and check you’re on the correct one for your requirements.
  3. Check that you’ve got page caching enabled. Even if you have cheap web hosting, a good page-caching plugin will help a lot.

After the browser has loaded the initial HTML, it starts requesting all the other assets like JavaScript files, fonts, CSS and images. In our example, you can see that I’m using the Autoptimize plugin to aggregate my JS & CSS files into fewer/combined files.

As you move down the waterfall, there’s an unusual plot near the end, “POST admin-ajax.php“. Here’s what that’s all about:

  • After the document becomes ready, I’ve got some JavaScript in there that does an Ajax callback to find out how many up/down votes the post has.
  • This request-response is quite slow (500+ milliseconds) because we’re not loading a cached asset. We need to execute some PHP that does some work on the server and returns the result. Pages that have WooCommerce floating-cart buttons or mini-carts in the primary navigation work the same way, and can reduce the page-load speed.

Generally speaking, the above waterfall looks clean. The overall speed is good and GTmetrix has given as a score of 100% for performance 😎.

A slower page-load

If we disable Autoptimize and page-caching then rerun the test, you can see the difference it makes:

Page load waterfall without asset minification and page caching
A network request/response waterfall without asset minification and page caching

Check out that top line. The initial request-response for the page’s HTML has jumped from 84ms to 635ms. That’s because WordPress has to execute a ton of PHP to create the HTML before it can return it to the browser (i.e. it’s not cached).

There are also lots of additional requests and responses beneath it because we’ve not aggregated any JS & CSS assets. Each asset has to be requested individually, instead of them being smushed together into a couple of aggregated files.

tip In short… always use a good page caching plugin, aggregate your asset files and use good WordPress hosting on servers that are geographically close to your audience. These are by far the biggest steps you can take to speed up your WordPress site.

Remove unwanted assets

If you want to dig into page-load optimisation, we can look at removing some of the assets too. Most WordPress plugins that affect the front-end will ship with some CSS and/or JavaScript, and they often include these assets in every page-load on your site… even if you only use the plugin’s feature on one page.

Let’s say you’ve got a contact page and you’re using a reCaptcha to reduce spam. You don’t need the reCaptcha’s JavaScript code loading on every page… you only need it on the contact page. Adding the reCaptcha to the front page is just slowing down your site for no good reason.

To see the assets on any page load, use your browser’s “View Page Source” function. Scroll down a bit and you should see a big chunk of <link.../> and <script... /> elements:

<!DOCTYPE html>
<html lang="en-GB">
<head>

	<!-- ...SNIP... -->

	<link rel='stylesheet' id='intltel-css' href='https://wp-tutorials.tech/hwh/intltel/intltel-public.css?ver=2.13.8' media='all' />
	<link rel='stylesheet' id='contact-form-7-css' href='https://cdn.wp-tutorials.tech/wp-content/plugins/contact-form-7/includes/css/styles.css?ver=5.7.2' media='all' />
	<link rel='stylesheet' id='cas-frontend-css' href='https://cdn.wp-tutorials.tech/wp-content/plugins/wpt-addons//custom-ajax-search/cas-frontend.css?ver=2.0.2' media='all' />
	<link rel='stylesheet' id='scroll-to-top-css' href='https://cdn.wp-tutorials.tech/wp-content/plugins/wpt-addons/wpt-scroll-to-top/scroll-to-top.css?ver=2.0.2' media='all' />
	<link rel='stylesheet' id='wpt-auto-toc-css' href='https://cdn.wp-tutorials.tech/wp-content/plugins/wpt-addons/wpt-auto-toc/auto-toc-frontend.css?ver=2.0.2' media='all' />

	<!-- ...SNIP... -->

	<link rel='stylesheet' id='hw-font-awesome-5-css' href='https://wp-tutorials.tech/hwh/fontawesome/font-awesome-5.min.css?ver=5.15.3' media='all' />
	<link rel='stylesheet' id='mini-gdpr-cookie-consent-css' href='https://cdn.wp-tutorials.tech/wp-content/plugins/mini-wp-gdpr/assets/mini-gdpr-cookie-popup.css?ver=1.2.10' media='all' />
	<script src='https://cdn.wp-tutorials.tech/wp-includes/js/jquery/jquery.min.js?ver=3.6.1' id='jquery-core-js'></script>
	<script src='https://cdn.wp-tutorials.tech/wp-includes/js/jquery/jquery-migrate.min.js?ver=3.3.2' id='jquery-migrate-js'></script>
	<script src='https://cdn.wp-tutorials.tech/wp-content/plugins/wpt-addons/public/js/wpt-addons-public.js?ver=2.0.2' id='wpt-addons-js'></script>
	<script id='wpt-auto-toc-js-extra'>
		var autoToc = {"heading":"Quick Jump","anchorHeadingsSelector":".dynamic-entry-content h2[id]","allHeadingsSelector":".dynamic-entry-content > h2","scrollAnimDuration":"1000","autoShow":""};
	</script>
	<script src='https://cdn.wp-tutorials.tech/wp-content/plugins/wpt-addons/wpt-auto-toc/auto-toc-frontend.js?ver=2.0.2' id='wpt-auto-toc-js'></script>
	<!-- ...SNIP... -->

</head>

<body class="...">

	<!-- ...SNIP... -->

</body>

</html>

Each of the link and script elements has an id that ends with a “-css”, “-js” or “-js-extra” suffix. Everything before the suffix is called the handle, which we can use to “dequeue” the assets if we want to.

In the above HTML snippet, we’ve got some assets that use the “wpt-auto-toc” handle from our automatic table-of-contents tutorial. If we add the following code to our child theme‘s functions.php, it will remove the “wpt-auto-toc” assets from all posts in the “optimise-wordpress” category.

function custom_conditionally_strip_assets() {
	if (is_single() && in_category('optimise-wordpress')) {
		wp_dequeue_script('wpt-auto-toc');
		wp_dequeue_style('wpt-auto-toc');
	}
}
// Priority 99 means we want our callback to be executed "late" instead of default (10) or "early".
add_action('wp_enqueue_scripts', 'custom_conditionally_strip_assets', 99);

Most actions are hooked with a priority of 10 (using add_action()), although this number isn’t really a priority… it represents the sequence that the action hooks are triggered. The tutorial code for the automatic table-of-contents widget enqueues its assets with a priority of 10 (default), so we need to trigger our code (to dequeue the assets) later in the sequence by passing a value higher than 10. Technically we could use 11, but it’s standard practice to use a value of 99 when you want to hook something “late” in an action sequence.

If we called wp_enqueue_scripts with a priority/sequence of 5, nothing would change because the code to enqueue the assets would be triggered after our code to dequeue them.

We can extend the above code to do all sorts of things. Say you’ve got a reCaptcha plugin that uses the handle “funky-recaptcha” to inject CSS and JS into every page of your site, but you only need the reCaptcha on a single page (e.g. post_id=67), you could do this:

function custom_conditionally_strip_assets() {
	// Remove the reCaptcha assets from all content apart from
	// the contact page (post_id=67)
	if (!is_page() || (get_the_id() != 67)) {
		wp_dequeue_script('funky-recaptcha');
		wp_dequeue_style('funky-recaptcha');
	}
}
add_action('wp_enqueue_scripts', 'custom_conditionally_strip_assets', 99);

Now you can just work your way through the CSS & JS assets in your page’s <head>...</head> and dequeue anything that looks unnecessary.

Here’s a neat way of stripping multiple assets from your site’s front page by their handle:

function custom_conditionally_strip_assets() {
	if (is_front_page()) {
		$handles_to_dequeue = array(
			'funky-recaptcha',
			'font-awesome-4',
			'wpt-auto-toc',
		);

		foreach ($handles_to_dequeue as $handle_to_dequeue) {
			wp_dequeue_script($handle_to_dequeue);
			wp_dequeue_style($handle_to_dequeue);
		}
	}
}
add_action('wp_enqueue_scripts', 'custom_conditionally_strip_assets', 99);

Baby steps, rinse and repeat

Try not to make too many changes at once, and make sure you keep testing as you go.

When I’ve built a site that’s ready to publish, I’ll optimise the front page by removing unnecessary assets one at-a-time… I’ll remove an asset, retest either with Lighthouse (in the Chromium browser), or with GTmetrix. When I’m satisfied the page still works correctly, I’ll remove another extraneous asset, and keep going until the speed is good.

importantAlways flush the page cache after changing your asset-aggregation or page-caching settings, and don’t be afraid to rip out loads of CSS & JS that looks superfluous. Most WordPress pages have far too many assets on them and they can be safely dequeued. If you dequeue an asset and the page breaks, just don’t dequeue that one and try another one instead.

Have fun aiming for that top spot of 100% performance. Chrome’s Dev Tools Lighthouse gives you a little fireworks animation when you get 100 for performance, accessibility, best practices and SEO 🎇 🎇 🎇 😎

Like This Tutorial?

Let us know

WordPress plugins for developers

Leave a comment