WordPress Performance - Breaking It down by HTTP Requests
WordPress can be a tricky beast as they say when it comes to web performance. Especially if you are comparing it against others running static sites. Because WordPress is based on PHP and most of the content is dynamically generated, it means you need to find other ways to optimize. In today's post, we are going to dive into a fresh WordPress install and take it apart piece by piece, or rather by HTTP requests, to hopefully help you gain a better understanding of WordPress performance. WordPress can load a lot faster than what some people might lead you to believe!
Breaking down WordPress performance
In this post, we are going to first tear down a fresh install and then afterwards make some improvements to it. Make sure to also check out our speed up WordPress guide for additional tips on improving WordPress performance. In this case study, we are running on NGINX, over HTTPS and HTTP/2. If you are starting a new website, we encourage you to take advantage of HTTP/2 right off the bat. Or check out our HTTP to HTTPS migration guide.
Understanding the basics when it comes to web performance makes it easier to apply advanced methods later.
Our fresh WordPress site is using the default Twenty Sixteen theme. Let's first take a look at each HTTP request that is generated out of the box, as there are a couple you can probably get rid of. So by default, we have a site with 15 HTTP requests at a page size of 135 KB. And a decent load time. Remember, as you add media and content to your site your load time will only increase, so it is better to optimize your site as much as possible first, and then add content. Also, if you are starting with a different base theme, you can apply almost everything we do below as well, although it might vary slightly.
Step 1 - Disable emojis
The first one is the wp-emoji-release.min.js
file. This file is used for showing emoji characters in WordPress, since version 4.2. Unless you absolutely can't live without emojis, we recommend this is the first thing to get rid of on a fresh install. To disable emojis you have the following to options:
1. Disable emojis with code
The first way to disable emojis is you can put the following code into your functions.php
file:
/**
* Disable the emoji's
*/
function disable_emojis() {
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
remove_action( 'admin_print_styles', 'print_emoji_styles' );
remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
add_filter( 'tiny_mce_plugins', 'disable_emojis_tinymce' );
}
add_action( 'init', 'disable_emojis' );
/**
* Filter function used to remove the tinymce emoji plugin.
*
* @param array $plugins
* @return array Difference betwen the two arrays
*/
function disable_emojis_tinymce( $plugins ) {
if ( is_array( $plugins ) ) {
return array_diff( $plugins, array( 'wpemoji' ) );
} else {
return array();
}
}
2. Disable emojis with plugin
If you prefer to use a free lightweight plugin, Ryan Hellyer has created a great Disable Emojis plugin for WordPress. The total size of the plugin is only 9 KB.
Remember, with plugins it isn't always bad to have a lot of them running. It is more about quality of the plugin vs quantity. If a plugin is lightweight, it won't slow your site down. Essentially all this plugin is doing is running the code we shared above. After doing this we are now left with 14 HTTP requests at a total size of 129 KB.
Step 2 - Disable embeds
Next, we will look at the wp-embed.min.js
script. This has been included in WordPress since version 4.4. This is a script that auto formats pasted content in the visual editor, such as videos, tweets, etc. However, this is not really needed. A big issue with this script is that it loads on every single page, whether it is being used or not. You can still use the default embed code from YouTube and Twitter to included content, even when this script is disabled. Again we have two options:
1. Disable embeds with code
The first way to disable embeds is you can put the following code into your functions.php
file:
// Remove WP embed script
function speed_stop_loading_wp_embed() {
if (!is_admin()) {
wp_deregister_script('wp-embed');
}
}
add_action('init', 'speed_stop_loading_wp_embed');
2. Disable embeds with plugin
If you prefer to use a free lightweight plugin, Pascal Birchler has created a great Disable Embeds plugin for WordPress. The total size of the plugin is only 3 KB and features the following:
- Prevents others from embedding your site.
- Prevents you from embedding other non-whitelisted sites.
- Disables all JavaScript related to the feature.
After doing this we are now left with 13 HTTP requests at a total size of 128 KB. You can also see that our DOMContentLoaded and total Load time keeps decreasing as we make these WordPress performance tweaks. We are using Chrome DevTools and have caching disabled.
Step 3 - Implement caching
Notice above in all of our speed tests that the first HTML initial DOC load is quite high. Even when we enable caching in Chrome DevTools it still hovers around 180 ms. So to fix the initial HTML DOC load time, you need to implement a caching mechanism. In this case, we are going to use the free WordPress Cache Enabler plugin from KeyCDN.
Cache Enabler is a lightweight caching plugin for WordPress that makes your website faster by generating static HTML files to disk. This is a very fast method of caching. The plugin also focuses on HTTP/2, meaning you won't find options for concatenation, as this can hurt HTTP/2 performance. The total size of the plugin is only 134 KB. In the Cache Enabler plugin settings we recommend enabling both pre-compression and HTML minification.
Now, lets look at our first HTML initial DOC load again. Simply by enabling Cache Enabler, this has dropped us from 180 ms on average down to 80 ms on average. So it shaved off about 100 ms from our HTML DOC load. This is the power of what a caching plugin will do for your WordPress site. Also, our total page size dropped to 123 KB thanks to minification and again our DOMContentLoaded and total load time is lower.
Step 4 - Use a CDN
Now that we have our base fresh install fairly well optimized, now is the time to add a CDN into the mix. By using a CDN you can further decrease the latency by serving up the content closer to your visitors. Also, as we will show later on, there are additional benefits, such as moving towards a single HTTP/2 connection and ignoring query strings for additional caching.
Make sure to check out our in-depth posts on how a CDN works and also our getting started guide. In this post, we are going to assume you already have a CDN up and running. We are going to be using the free WordPress CDN Enabler plugin from KeyCDN to deploy our CDN. The total size of the plugin is only 6.4 KB.
In the CDN Enabler settings we enable HTTPS (as we are using HTTP/2 from KeyCDN) and input our CDN URL.
After we enable the CDN, you can see most of our assets (JavaScript, CSS, etc.) are now loading from our CDN. The total load time drops by about half, and our DOMContentLoaded time also decreases by a large amount. Notice how our waterfall, is getting nicer as we continue to optimize. Not sure what we mean by waterfall? Make sure to check out our in-depth post on waterfall analysis.
Step 5 - Remove query strings
Next, you notice how every script ends in a version number? Such as ver=1.12.4
or vers=3.4.1
. These are called query strings and help determine the version of the script. The problem with query strings like these is that it isn't very efficient for caching purposes and sometimes prevents caching those assets altogether. So you have a couple options here:
1. Remove query strings with code
The first way to remove query strings is you can put the following code into your functions.php
file:
function _remove_script_version( $src ) {
$parts = explode( '?ver', $src );
return $parts[0];
}
add_filter( 'script_loader_src', '_remove_script_version', 15, 1 );
add_filter( 'style_loader_src', '_remove_script_version', 15 );
2. Cache and ignore query strings with a CDN (recommended)
The third option and this is the recommended method is that if you are using KeyCDN, we have an option to actually ignore query strings and cache the assets regardless. This feature is enabled by default on all Zones. This means you don't need to use code or a plugin for this optimization, it is all handled by the CDN. If for some reason you need to disable it, it can be done in your Zone settings through the Ignore Query String setting.
Step 6 - Host Google Fonts
Next, if we look closer we can see there are 4 requests being generated to fonts.gstatic.com. And this is to load Google fonts, which is included in the default WordPress theme. In our example, it is loading different font weights for Merriweather and Montserrat.
https://fonts.googleapis.com/css?family=Merriweather%3A400%2C700%2C900%2C400italic%2C700italic%2C900italic%7CMontserrat%3A400%2C700%7C&subset=latin%2Clatin-ext
https://fonts.gstatic.com/s/montserrat/v7/IQHow_FEYlDC4Gzy_m8fcoWiMMZ7xLd792ULpGE4W_Y.woff2
https://fonts.gstatic.com/s/merriweather/v13/RFda8w1V0eDZheqfcyQ4EOgdm0LZdjqr5-oayXSOefg.woff2
https://fonts.gstatic.com/s/montserrat/v7/zhcz-_WihjSQC0oHJ9TCYPk_vArhqVIZ0nv9q090hN8.woff2
It always better to reduce the number of external DNS lookups and also focus on having a single HTTP/2 connection if possible. Every external lookup introduces its own set of latency issues, content download times, TLS negotiations, etc. So what we are going to do is move the Google fonts to our CDN. This way they load from the same place as the rest of our assets.
You can check out our in-depth tutorial on how to migrate Google Fonts to your CDN. This can also be used to simply host them directly on your web server as well, if you aren't using a CDN. We quickly download the following Google fonts from https://gwfh.mranftl.com/fonts
and host them on our server in a folder called "fonts."
merriweather-v13-latin-700.woff
merriweather-v13-latin-700.woff
merriweather-v13-latin-700italic.woff
merriweather-v13-latin-700italic.woff2
merriweather-v13-latin-900.woff
merriweather-v13-latin-900.woff2
merriweather-v13-latin-900italic.woff
merriweather-v13-latin-900italic.woff2
merriweather-v13-latin-italic.woff
merriweather-v13-latin-italic.woff2
merriweather-v13-latin-regular.woff
merriweather-v13-latin-regular.woff2
montserrat-v7-latin-700.woff
montserrat-v7-latin-700.woff2
montserrat-v7-latin-regular.woff
montserrat-v7-latin-regular.woff2
Because we are using the CDN Enabler plugin already, these fonts then are immediately copied to our CDN. To use them, we then have to disable Google fonts in our WordPress theme and apply the styles to our internal CSS style sheet. This will vary based on whatever theme you are using. If you can't figure out how to disable Google fonts on your current theme, feel free to ask your theme developer.
As you can see, we are now down 12 HTTP requests since we no longer have to query Google's style sheet. And our fonts are now loading from the CDN. You can see that our DOMContentLoaded and total load time also continue to drop. Every optimization we make has a crucial impact on performance.
https://cdn.themewood.com/fonts/montserrat-v7-latin-700.woff2
https://cdn.themewood.com/fonts/merriweather-v13-latin-regular.woff2
https://cdn.themewood.com/fonts/montserrat-v7-latin-regular.woff2
Step 7 - Disable Gravatars
As you can see we are almost down to a single HTTP/2 connection with no external DNS lookups. The only thing left is that call to gravatar.com. Thankfully they are using HTTP/2 now, but unless you really want avatars, you can disable them.
This can easily be remove by un-checking the "show avatars" in the discussion setting of your WordPress dashboard.
By removing the call to gravatar.com we are now down to 11 HTTP requests and are using a single HTTP/2 connection for external assets on our CDN.
And if we un-check the "disable cache" option in Chrome DevTools we can see for repeat visits the website loads very fast!
Summary
As you can see, when you break down a site by individual HTTP requests it helps you to understand better what is causing delays and how you can improve upon WordPress performance. You can then take this knowledge and apply it to bigger sites and more advanced WordPress themes. We have seen these simple techniques mentioned above even help bring WordPress themes like Avada (which is known for being very large) to load times of under 650 ms!