Along with security issues, one of the top reasons why people express doubts about WordPress is poor website performance. And as with security, the devil is in the details. While WordPress optimization may seem like a large and complex topic, in fact, it can be reduced to a manageable number of points that we just need to take care of.
This article is the first of a two-part series concerning WordPress performance optimization. In the first article, I wanted to define the problem and discuss the back-end side of the optimization (split into a network and main document parts). The second article will be focused mostly on front-end optimization (template, assets).
What do we mean by performance
To put it simply, we want to achieve the shortest possible delay before the requested web page of our site is completely displayed to the visitor and is fully interactive as expected. Load time influences more than just user comfort. For example, did you know that high load times correlate to fewer pages being indexed by the Google crawler?
There are other performance aspects of course. Google introduced Core Web Vitals, which I explain in a solo article. While load time is still a very important factor, other usability metrics such as First Input Delay or Cumulative Layout Shift can also be measured, but we will not pursue them here. For the sake of simplicity, we will focus mostly on the website load time, which is approximately correlated with Largest Contentful Paint from the CWV.
To understand what website load time essentially consists of, we first need to have a general knowledge of how your web browser interacts with a website.
Steps to take when the website is loaded
You can find a lot of suitable explanations for this problem on the internet. Let’s be as simple as possible and divide the load process into three parts. I have taken the liberty of using my own terms for these parts - don’t be alerted if you come into contact with different terms, as they often encompass different areas. Also, the three parts do not exist in isolation - for example, the network part and front-end part, especially asset loading performance, are heavily interlinked.
-
Network part. This is time spent on a “chat” that your web browser is having with a web server, about all prerequisites before the main document loading is started. Things like Network latency, DNS speed, SSL parameters, any redirects before loading the document all matter here.
-
Main document part. Here, what concerns us most is the so-called TTFB (Time To First Byte). This is essentially the delay that the web server is experiencing before serving the main document. WordPress setup influences this part heavily. Following up is the Content download time, which would be significant for web pages with large contents (thus serving as an argument for making it smaller).
-
Front-end part. Once the main document is provided to the browser and being processed by it, the actual rendering of the document and fetching of the assets starts, and this part can be attributed as a front-end responsibility. Template and asset optimization matters here.
Network: Think about the service
Let’s explore some areas that you can focus on regarding the network part. This is mostly about a thoughtful approach towards the services that your website will be served on.
DNS - performance of your DNS provider matters when first contact with your site is established. DNS service for your domain is usually handled within your domain registrar, who in most cases have a default one to use for your parked domains. This can be upgraded to standalone DNS services such as Amazon Route 53 for extra costs, solutions like Cloudflare offering extra security protections, or you can switch to DNS service coupled with your host solution if it is optimized well. Some more research into them will tell you if the advantages are worth the extra price.
Latency - choosing a provider geographically close to your customers is important. Data packets can travel close to ⅔ the speed of light in the fibre optic cables, which would theoretically suffice, but you have to factor in additional routing time for larger distances, as the amount of hops (passing of packets from one network to another) increases. There is a big difference in load time if latency is changed from 25 ms (which is normal for close service providers) to 250 ms (overseas infrastructure), as demonstrated in a simple test. Should you want to have a truly global website, don’t rely on one server and invest instead into solutions like CDN. To further improve latency, some hosts allow you to use premium tier networks to deliver your requests and responses faster than normal traffic would go.
HTTP/2 - whenever possible, your server should offer this protocol over the older HTTP/1.x. It is a completely new, backwards-incompatible protocol making great use of streams, multiplexing and header compression to achieve lower latency and thus higher throughput. It negates one of the biggest issues HTTP/1.x had, having to perform an individual request for every asset on your website, bearing a lot of network overhead. That led to complex optimization strategies in the past, such as asset concatenation, which is not needed anymore. HTTP/2 must be enabled on your web server if it’s capable of it, so it depends on your host configuration.
While improving the network might initially seem like an over-the-top optimization, there are still arguments where it makes some sense. It might be the important lower hundreds of milliseconds that are blocking your potential customers by slowing their page load. Especially sites that attract a lot of new visitors daily should consider starting with that.
As an example of local/global optimization, we can illustrate it on our own kurzor.net website. An important DNS feature for going global is Anycast, which is a big helper to global websites. This is basically a methodology that speeds up network latency by offering connection points nearest to the visitor. So it requires an infrastructure that can be spread over multiple physical locations (which is exactly what the largest providers like AWS or Google offer). Often this is a win-win situation for the client and the service provider, as the provider can spread the load evenly around the globe. We use Anycast as a part of Route 53 and it significantly affects DNS lookup time - see Figure 2 for an interesting comparison.
Main document
Generating the main document quickly is the responsibility of your web server and nothing else. The main value we want to decrease as much as possible is TTFB. This can be achieved by three basic means: optimizing WordPress setup, upgrading host environment performance, and employing caching.
For testing purposes, I have used a simple one-page website from our 2020 production. It has a long content composed of various data types, containing a number of media assets, which could be expected in today’s websites. It has a custom-built template from a bespoke design. The site was relatively un-optimized from the perspective of main document loading, which makes it a good candidate to test optimizations on.
What can you do in WordPress
Since the main document is generated by WordPress, it is important to remove any potential blockers from the page load. That in turn requires developers skilled in understanding how WordPress internals work and how it interacts with plugins and themes. And even if this is tricky, it still should be a preferred way to start your optimization process, as it can bear the most effective result: there is no need to cache or upgrade the performance of your web host if there is a manageable amount of operations happening during page load.
Plugins might be the usual suspects for a TTFB slowdown. Worst contenders might do multiple database calls and file system searches on every page load, and that obviously steals tens of milliseconds.
So start your optimization by carefully inspecting your plugin set, and try to answer these questions:
- Do I really need this plugin?
- If so, does it influence my page load time at all?
These are not simple questions and an experienced developer is needed to answer them. Things like profilers can help here. But just simply enabling/disabling plugins and measuring changes to TTFB could provide a helpful hint.
The good thing is you can always find a large number of alternatives for the most common functionality in the WordPress repository.
A few other problems might exist in your WordPress installation, especially in a long-running one:
- wp_options table might become too large with a lot of entries auto-loaded, which can slow down any page request. A cleanup is then in order.
- Plugin auto-updates might cause your WordPress admin to completely lock up during loading.
Analyzing our testing site, we see it already is composed of a minimal amount of plugins required, that will affect page load:
- Advanced Custom Fields Pro,
- Contact Form 7,
- Siteguard WP Plugin,
- Wordfence security plugin.
So we are on a bare minimum here. Of course, Advanced Custom Fields could be replaced by defining our content fields in the template, but that would be an “optimization too far”, applicable only if the amount of custom fields is too large.
Regarding the wp_options table, we have 180 entries total and 148 of them are auto-loaded. Again, not much to optimize here.
Your host matters
If you feel that your WordPress installation is clean and updated and nothing else can be removed, it might be a good idea to review your web host.
Usually, your web host solution will be one of the following:
- generic shared web host,
- custom VPS or server,
- dedicated managed WordPress hosting.
Generic shared web host is usually the cheapest. The problem is that it produces inconsistent outcomes, and given how its business model works, you might end up with good or very poor results regarding TTFB. From its nature, if hosting is shared, its performance depends on the number of websites on a given machine and on the visitor load they receive. One site with large traffic can easily slow down the rest and you have no way to influence this.
Custom VPS or server depends a lot on the specs (hardware) and the setup (software). This requires support personnel experienced in the SysOps area. Don’t forget to include regular maintenance and updates in this case, this is not a “launch and forget” kind of operation.
Managed WordPress hosting is usually the most expensive option regarding regular payments, but offers significant advantages:
- Is optimized to run WordPress,
- usually has a solution for a performant network part
- might offer built-in caching or CDN options,
- might deal with spikes in traffic with relative ease,
- has customer support oriented for WordPress issues,
- usually has a way of creating staging environments easily.
Interested in WordPress managed hosting? You might want to explore:
- Kinsta - state-of-the-art managed WordPress hosting,
- WP-Engine - very well known and popular,
- Flywheel - has some interesting collaboration mechanisms.
On your web host, important things to check are:
- The server software itself. Using nginx over Apache could be a game-changer especially on high loads.
- PHP version - 5.x versus 7.x versus 8.x have notable performance changes
- MySQL optimization - for example just changing table type to InnoDB has a great impact.
To see how web host configuration matters, we hosted our testing website on two well-known Czech providers, Forpsi and Onebit, in four instances:
- Forpsi 1 - an older hosting from 2020 - Webhosting Easy, Linux variant, PHP 7.3.17
- Forpsi 2 - new hosting for test purposes - Webhosting Easy, Linux variant, PHP 7.4.6
- Onebit 1 - new hosting - HOME plan, PHP 7.3.31
- Onebit 2 - new hosting - HOME plan, PHP 8.0.13
We also experimented with enabling output compression - meaning the HTML output is prepared and sent in a gzip-compressed form to save content download time. See Table 1 for what we collected:
Instance | Output compression | TTFB | Content Download |
---|---|---|---|
Forpsi 1 | - | 743 (± 510) ms | 20.5 ms |
Forpsi 1 | gzip | 269 (± 52) ms | 11.2 ms |
Forpsi 2 | - | 231 (± 96) ms | 19.3 ms |
Forpsi 2 | gzip | 232 (± 95) ms | 9.43 ms |
Onebit 1 | - | 261 (± 29) ms | 15.2 ms |
Onebit 1 | gzip | 248 (± 15) ms | 7.2 ms |
Onebit 2 | - | 231 (± 44) ms | 15.1 ms |
Onebit 2 | gzip | 223 (± 41) ms | 7.2 ms |
Table 1: Comparison of two different web hosts and their configuration changes. Results are a median over 10 independent measurements in the anonymous browser tab, no cache plugin was in effect. We also computed a standard deviation (in brackets), to better spot result sets with high differences.
We cannot safely make points about the differences without detailed knowledge of how the hosts operate internally. There are a few observations, though:
- You can see the results are relatively close for both hosts. The only outlier is the older Forpsi 1 configuration with gzip disabled, which differs by a lot, producing extreme variance.
- Interestingly, the same instance with gzip enabled produces results in line with other instances.
- You can also see how newer PHP versions generally tend to produce slightly better results.
- Content download time predictably shrinks for gzip output compression enabled, this is nicely demonstrated by all instances.
- You would expect gzip output compression to actually increase TTFB due to the additional complexity of encoding. That is not the case in measured results, however. I would make a guess that gzip output compression is heavily optimized in today’s hosts, which is backed up by the achieved results.
WordPress caching plugins: Contenders and test
Last but not least, a great way to influence TTFB is to prepare the loaded result in advance and serve it very quickly, when asked. This is called caching. It could be done automatically on the server level (Kinsta does that), or can be enabled via a plugin.
Caching is like taking a shortcut. It is an ideal solution for sites that don’t update often. If your site produced different content on every load, caching would make no sense. But storing prepared rendered copies of your site pages in advance is a great strategy for most sites to decrease TTFB significantly.
With caching, always remember to:
- Make sure you have a mechanism to clear the cache on demand. This will come in handy when you do a lot of changes and caching might produce inconsistencies for the visitors.
- Set some reasonable expiration mechanics on your cache. It is easy to persuade your browser into thinking that your site will be immutable forever, but that would make it very hard to update in the future.
- Caching should be done when it makes sense, for the most visited pages on your website. Some parts, such as the fourth page of a paginated product listing, might not be the case and it adds strain to the caching mechanism.
The test. I have analyzed 5 well-known plugins, which are free to use. I have not tested premium plugins or features, although a lot of them offer some form of paid advantage, like using their CDN. WP Rocket is one of the premium examples.
I focused on enabling similar features in the cache plugins (of course, this can not be considered 100 % fair as the plugins differ): the main goal was to test page cache, which means serving the whole home page of our site from a statically prepared version. When there was some interesting optimization, such as minification or compression, I enabled it as well. Configuration details are specified below.
Let’s introduce the tested five.
W3 Total Cache
- Plugin to be found here: W3 Total Cache
- Any list of WordPress cache plugins would be incomplete without it: a very popular choice
- Offers not only page cache, but also caching of CSS, JavaScript, feeds, database objects, PHP objects, fragments etc.
- Has a range of front-end asset optimization features as well (multiple CDN integrations)
- Settings are configured via a reasonably clear wizard (see Figure 3)
Cache Enabler
- Plugin to be found here: Cache Enabler
- Main advantage that I observed is the pure simplicity of this plugin. You just activate it and it works. There are plenty of settings for advanced users, notable is WebP image generation support, which requires a 3rd party tool.
Comet Cache
- Plugin to be found here: Comet Cache
- Setting of this plugin is also very simple, and the form in which it does so is interestingly talkative (see Figure 5).
- It has quite a lot of settings regarding automatic cache clear in various scenarios.
WP Fastest Cache
- Plugin to be found here: WP Fastest Cache
- Settings are a bit overwhelming (see Figure 6) so it offers quite some playground for experiments.
- The premium version offers many more optimizations, especially for the front-end. However, a lot of the premium features seem to be contained in the other plugins we tested, for free.
WP Super Cache
- Plugin to be found here: WP Super Cache
- Very lightweight plugin with a clear focus on producing static HTML files to serve instead of waiting for WordPress to serve them.
- Settings are a bit harder to understand, but it has improved over the years (we have approx. 5 years of experience with this plugin). One of the hallmarks is a powerful .htaccess-only mode, in which it completely bypasses WordPress as such, but we never reliably achieved this on our hosts.
Testing and results
We have enabled the plugins and measured the TTFB impact. For this, we have used the previously mentioned Onebit web host, with PHP 8.0 set and gzip compression enabled. For every plugin, a clean copy of the environment was prepared, to mitigate any effects from previous plugin tests.
We have set the plugins the following way (Table 2):
Plugin | Settings |
---|---|
W3 Total Cache | Page cache: enabled/disk. DB cache: disk. Minify: auto/disk. Opcode: Zend OpCache. Object cache: disk. Browser cache: disabled. |
Cache Enabler | Minification of HTML only enabled. |
Comet Cache | Client-side cache: yes. Rest kept as default |
WP Fastest Cache | Defaults only (no checkboxes ticked) |
WP Super Cache | Simple method + defaults (see Figure 5) |
Table 2: Cache plugin settings.
Results were measured by 10 independent tries with a new anonymous browser tab. Several attempts were made before the measurement to account for page cache creation.
Numbers are in Table 3 - we included the non-cached entry for Onebit for comparison, in the last row:
Plugin | Median TTFB Result (± standard deviance) |
---|---|
W3 Total Cache | 28.94 (± 8.28) ms |
Cache Enabler | 26.79 (± 4.45) ms |
Comet Cache | 35.93 (± 4.63) ms |
WP Fastest Cache | 43.25 (± 7.44) ms |
WP Super Cache | 25.80 (± 4.36) ms |
Uncached | 223 (± 41) ms |
Table 3: Results of the cache tests.
Now, obviously, there are differences between the results, but you can see that page caching comfortably lands our TTFB into an area below 50 milliseconds, which is significantly faster! This is the result of bypassing a standard WordPress page loading logic. Therefore, page cache is for sure recommended by me for sites with relatively static content.
Conclusions & What to expect in Part 2
As I tried to point out in our test cases, the devil is in the detail:
- Optimize the Network part if you expect a lot of traffic and your current situation is not ideal,
- Do a clean-up of plugins on your site,
- Enable caching for mostly static sites + do not worry about using one of the free caching plugins.
In Part 2 of this series, I will focus more on what happens once the Main document is loaded to your browser: the processing of it and fetching of external data define the required time before our visitor can see the result. We’ll discuss template optimizations, compression, using CDN etc. Stay tuned!