A Look At The Modern WordPress Server Stack
Do you remember when you could run a “fast” WordPress website with just an Apache server and PHP? Yeah, those were the days! Things were a lot less complicated back then.
Now, everything has to load lightning-fast! Visitors don’t have the same expectations about loading times as they used to. A slow website can have serious implications for you or your client.
Consequently, the WordPress server stack has had to evolve over the years to keep up with this need for speed. As part of this evolution, a few gears have had to be added to its engine. Some of the older gears have had to change as well.
The result is that the WordPress server stack looks quite different today than it did a few years ago. To better understand it, we’re going to explore this new stack in detail. You’ll see how the various pieces fit together to make a WordPress website fast.
Overview
Before diving in, let’s zoom out and look at the big picture. What does this new WordPress server stack look like? Well, here’s the answer:
The diagram above gives a good overview of what the modern WordPress server stack looks like. At a high level, we can divide what’s going on into three areas:
- the request-response cycle between the browser and WordPress;
- WordPress (which is a script that the PHP runtime executes);
- the query-result cycle between WordPress and the MySQL database.
The role of the modern WordPress server stack is to optimize these three areas. These optimizations are what make everything load faster. And the best part is that there are several ways to do it. (Yay!)
In most cases, these optimizations involve installing new services on your server. Sometimes, these services will need the help of a plugin to interact with WordPress. There will also be times when you can get away with just installing a plugin. You’ll see a lot of different options throughout this article.
The Request-Response Cycle
Everything starts with the browser. Let’s say you want to view the home page of modern.wordpress-stack.org
. Your browser will start by sending an HTTP request to the web server that hosts it. At the other end, the web server will take your request and turn it into an HTTP response.
This first response should always be the HTML content of the home page of modern.wordpress-stack.org
(unless there’s an error). However, the job of your browser isn’t done. No, that home page still needs more files, the most common ones being CSS, JavaScript and images files.
So, the browser will send requests for those files. The web server will respond with the requested files (again, as long as there are no errors). This cycle will continue until the browser has enough information to render the home page. The faster this cycle happens, the faster the website will appear to load.
Now, this is an obvious simplification, but it’s how things work for the majority of WordPress websites.
Optimizing The Request-Response Cycle
All right, this brings us to the obvious question, How do we make a web server perform this cycle faster? This is a great question and is part of the reason why the modern WordPress server stack exists.
The stack exists because you can’t just make a web server faster. A web server is also a dispatcher. It can receive a request and just forward it to other services.
These other services are often the bottleneck of this request-response cycle. With WordPress, this bottleneck is PHP, which is why optimizing the request-response cycle comes down to two things. We want the web server to:
- receive as few requests as possible,
- forward as few requests to PHP as possible.
The modern WordPress server stack focuses on this last one. It wants to forward as few requests to PHP as possible. That’ll be the main goal for optimizing the stack.
We focus on the second goal because the stack can’t do much about the first one; it has no direct impact on it. The second one is addressed either by the web server’s configuration or by modern development techniques.
Stack Elements For The Request-Response Cycle
So, what are the stack elements that will help us reduce the requests forwarded to PHP? Well, two stack elements in particular will help us achieve that goal: the web server and the HTTP cache.
Web Server
We’ve been speaking quite a bit about web servers already. There are three big players in the web server space:
Together, these make up more than 90% of the market share of web servers on the Internet. We’re going to focus on Apache and nginx. While IIS can be used to run WordPress, it’s not common because it’s available only for Windows, and most WordPress servers use Linux.
This leaves us with Apache and nginx. For all of WordPress’ life, Apache has been the recommended web server. We had the LAMP stack (Linux, Apache, MySQL and PHP), which ran WordPress on both the computer and the server.
But behind the scenes, things were changing. There was a new player in town, and its name was nginx. Automattic and WordPress.com have been using it since 2008. It’s the web server that runs the largest percentage of high-traffic websites (a lot of which run WordPress). That’s why a lot of high-end hosting companies and top WordPress agencies use it as their web server.
It’s not that Apache is a bad web server. There are Apache experts who can make it run great under a lot of traffic. It just doesn’t do as well out of the box or with the standard WordPress configuration.
Meanwhile, nginx’s sole purpose is to handle a lot of traffic. That’s why Igor Sysoev started the project when he was working at Rambler.
One of the reasons why nginx handles more traffic better is that it uses FastCGI to communicate with PHP. What is FastCGI? It’s a protocol that lets PHP run as a service separate from the web server.
Apache doesn’t do this by default. Each time the web server receives a request, it needs to start a PHP runtime process — even for images, JavaScript and CSS. This reduces the number of requests that the server can handle and how fast it can handle them.
This goes against one of the goals of the modern WordPress server stack, which we saw earlier. The stack needs to keep the request-response cycle time as low as possible. Loading PHP for every request, even when it doesn’t need it, goes against this goal. So, if you use Apache, look into FastCGI.
HTTP/2 is another important web server feature you should know about. It’s the next version of HTTP, the protocol that powers our entire request-response cycle.
Before the arrival of HTTP/2, a browser could have only six connections to the web server. And each connection could handle only one request at a time. So, in practice, a request-response cycle was capped at six requests per cycle.
That’s a real problem. Most websites have dozens of requests in their cycle. Developers and system administrators found clever ways to get around this limitation.
One of the better-known workarounds is the practice of combining CSS and JavaScript files. In an ideal scenario, this would bring down the total number of requests for CSS and JavaScript files to two: one for JavaScript and one for CSS.
This isn’t necessary with HTTP/2. HTTP/2 allows an unlimited number of requests per connection. This allows all extra requests after the initial HTML response to happen at the same time.
This has huge performance implications. A lot of the work of optimizing the server stack centers on the request-response cycle. By reducing the number of cycles to only a handful, HTTP/2 has done a tremendous amount of work for us.
HTTP Cache
The most important part of the modern WordPress server stack is the HTTP cache. In the WordPress world, we also call this page caching. The purpose of the HTTP cache is to cache responses to requests. What does this mean?
Well, let’s go back to our earlier example. The browser sends a request for the home page of modern.wordpress-stack.org
, and the web server receives that request and forwards it to PHP.
The problem with this scenario is that the web server is dumb. It’ll always forward all requests it receives to PHP — regardless of whether most of the requests would generate the same response.
This is exactly what happens when visitors aren’t logged in. To the web server, they’re all different requests, but it doesn’t care. It will forward them all to PHP, which will generate the same response for all of them.
That’s terrible! As we saw earlier, PHP is the real bottleneck of our request-response cycle. Your browser can’t send its follow-up requests until it receives that initial home page response. We can’t have the web server forward everything to PHP by default.
That’s where the HTTP cache comes in. It sits between the web server and PHP. Its job is to check every request that the web server receives and look for a cached response. If there isn’t any, it’ll forward the request to PHP and then cache the response that PHP generates.
This drastically reduces the request-response cycle time, making the website load faster. It also lets the web server handle more simultaneous requests without blowing up.
The Different Flavors Of HTTP Cache
At this point, you should be wondering, “How can I get this baby on my server ASAP?!” The good news is that installing an HTTP cache on a WordPress server is quite easy. It’s the component with the widest range of options.
Install A Page-Caching Plugin
The easiest way is to install a page-caching plugin. You have a few options to pick from:
All but WP Rocket are available as free plugins in the WordPress directory. So, you can install one and test it out right away. That being said, of the four plugins, the best one is WP Rocket. While it’s a paid plugin, it does a lot more than just create an HTTP cache. These other benefits magnify the work that the HTTP cache is doing.
How Does A Page-Caching Plugin Work?
All of these plugins leverage a drop-in that WordPress has made available for caching. This caching drop-in lets the plugins create an HTTP cache system inside of WordPress. The caching drop-in needs two things to work.
First, the advanced-cache.php
drop-in file needs to be in the wp-content
folder. That’s the actual file. But unlike most WordPress drop-ins, this one doesn’t kick in by default. WordPress also needs the WP_CACHE
constant to be true
in order to load the drop-in. In most cases, you would set that in wp-config.php
.
The diagram above shows what happens when you enable the drop-in with a caching plugin. WordPress loads the drop-in in wp-settings.php
during its loading process. This is early enough in the process that WordPress hasn’t done anything time-consuming yet.
The caching plugin will then check whether it has already cached the response to the request. If it has, it will return that cached response. If it hasn’t, it will turn on PHP output buffering, and WordPress will continue loading.
Now, output buffering is an interesting system. What it does is capture all the output from a PHP script in a string variable until one of two things happen:
- you tell PHP to stop buffering its output using one of the built-in functions,
- the PHP script finishes and needs to return a response to the browser.
The caching plugin is counting on the latter scenario. It wants WordPress to do its thing and then cache the whole output before PHP sends it back to the browser. You can see the process in the diagram below.
Have The Web Server Do It
The next option is to add an HTTP cache at the web server level. The way it works is that the web server will cache all responses to requests that get forwarded to PHP. This solution is better than a caching plugin because it doesn’t need to touch PHP at all.
The diagram above gives an overview of what’s going in the web server. The web server receives a request and checks with the HTTP cache. If a response has been cached already, the HTTP cache will send that back.
Otherwise, it will forward the request to the PHP module (usually FastCGI). It will pass the request to WordPress so that it can generate a response. The HTTP cache module will then cache that response on the way back.
Both Apache and nginx come with the ability to add an HTTP cache system. The nginx one is built in. The Apache one is a separate module that you need to add to your Apache installation.
There isn’t a lot of information on how to use the Apache module with PHP or WordPress. That might be because it’s not popular with the Apache crowd, or maybe because it has some issues. There’s at least one long-standing issue with it that is still open.
Meanwhile, the nginx HTTP cache system is robust and well documented. You can use it as a normal HTTP cache or as a smaller yet effective micro-cache. It’s just one more reason why nginx is the preferred web server nowadays.
Add Varnish To The Stack
What is Varnish? It’s a dedicated HTTP cache server (or, as its developers like to call it, HTTP accelerator). Most high-traffic websites and premium hosting companies use it as their HTTP cache solution.
They use it because it’s powerful and offers the most flexibility. Varnish has its own configuration language, called VCL. It lets you control every element of the caching process. Varnish also comes with a lot of tools for analyzing what the cache is doing and how it’s performing.
These are the major differences between using it and just using the built-in web server HTTP cache. The built-in web server HTTP cache is super-performant but also pretty basic. You don’t have a lot of control beyond a few configuration options.
Nevertheless, this power and flexibility come at a price. Varnish is also the most complicated HTTP cache option. It does nothing but cache HTTP responses. It doesn’t handle SSL termination, which most WordPress developers want (or should want). This means that our modern WordPress server stack is going to be more complex when we use it.
The diagram above illustrates this extra complexity. We now have two more components in our WordPress server stack: Varnish and a reverse proxy.
The reverse proxy is there to overcome the limitation that Varnish has with SSL. It sits in front of Varnish and decrypts the requests that our server receives. You can also call this type of reverse proxy an SSL termination proxy. The proxy then sends these decrypted requests to Varnish to process.
Once a request hits Varnish, the VCL configuration file(s) kick in. They’re Varnish’s brain. For example, they tell it how to:
- analyze, clean up and modify incoming requests;
- look for a cached response;
- analyze, clean up and modify returning responses from WordPress;
- cache these returning responses;
- handle a request to remove one or more responses from the cache.
This last one is especially important. Left to itself, Varnish has no way to know when WordPress wants to remove a page from the cache. So, by default, if you make changes to a post and update it, visitors will keep seeing the same cached page. Luckily for us, a plugin exists that removes pages from the Varnish cache.
WordPress
All right, our request for the home page of modern.wordpress-stack.org
has hit WordPress. It went through the request-response cycle that we just covered. The HTTP cache did everything it could to find an HTTP response to send back.
But there was no cached HTTP response to send back to the browser. At that point, the HTTP cache had no other choice. It had to forward the HTTP request to WordPress.
It’s all in WordPress’ hands now. WordPress has to turn our HTTP request into an HTTP response and send it back to the HTTP cache. As we saw earlier, this is the main bottleneck of our whole modern WordPress server stack.
The cause of this bottleneck is two-fold. WordPress has a lot PHP code to execute. This is time-consuming, and the slower PHP is at doing it, the longer it takes.
The other bottleneck is the database queries that WordPress needs to perform. Database queries are expensive operations. The more there are, the slower WordPress gets. This will be the focus of the last section on the query-result cycle.
Optimizing The PHP Runtime
Let’s get back to PHP. At this moment, WordPress has a minimum requirement of PHP 5.2. This version of PHP is almost 10 years old! (The PHP team stopped supporting it in 2011.)
The PHP team hasn’t been sitting idle all those years. Numerous performance improvements have been made, especially over the last few years. Let’s look at what you can do to optimize it nowadays.
Use The Latest Version Of PHP
The easiest thing you can do is upgrade your version of PHP. Versions 5.4, 5.5 and 5.6 all saw performance improvements. The largest improvement was from 5.3 to 5.4. Switching to it increased the performance of WordPress by a decent amount.
Install Opcode Caching
Opcode caching is another way to speed up PHP. As a server-side scripting language, PHP has a big flaw: It needs to compile a PHP script each time it executes it.
The solution to this problem is to cache the compiled PHP code. That way, PHP doesn’t have to compile it each time it executes it. This is the job of the opcode cache.
Before PHP 5.5, PHP didn’t come bundled with an opcode cache. You had to install it yourself on the server. This is another reason why using a more recent version of PHP is better.
Switch To A Next-Generation Compiler
The last thing you can do is switch to one of the two next-generation compilers: either Facebook’s HHVM or PHP 7, the latest version of PHP. (Why PHP 7? It’s a long story.)
Facebook and the PHP team built these two compilers from the ground up. They wanted to leverage more modern compilation strategies. HHVM uses just-in-time compilation, whereas PHP 7 uses ahead-of-time compilation. Both offer incredible performance improvements over good ol’ PHP 5.
HHVM was the first one to arrive on the scene a few years ago. A lot of top-tier hosts have had a lot of success with it, offering it as their primary PHP compiler.
It’s worth stressing, though, that HHVM isn’t an official PHP compiler. It’s not 100% compatible with PHP. The reason is that HHVM isn’t just designed to support PHP; it is also a compiler for Facebook’s Hack programming language.
PHP 7 is an official PHP compiler. It hasn’t been around for a long time. The PHP team released it in December of 2015. This hasn’t prevented some WordPress hosting companies from supporting it already.
The good news is that WordPress itself is 100% compatible with both compilers! The bad news is that not all plugins and themes are, because the minimum PHP version for WordPress is still 5.2.
Nothing is forcing authors to make their plugins or themes work with these compilers. So, you can’t go all in with one of them. Your stack should always fall back to PHP 5.
Query-Result Cycle
At this point, the PHP runtime is going through all of the WordPress PHP files and executing them. However, these WordPress PHP files don’t contain any data. They only contain the WordPress code.
The problem is that WordPress stores all of its data in a MySQL database. So, to get to it, the PHP runtime needs to query that database. The MySQL server returns the result of that query, and the PHP runtime then continues to execute the WordPress PHP files… well, that is, until it needs data again.
This back and forth can happen from a few dozen times to a few hundred times. (You might want to speak with your developer if it’s the latter!) That’s why it’s a major bottleneck.
Optimizing The Query-Result Cycle
The optimization goal here is to speed up the execution time of WordPress files by PHP. This is where database queries are problematic. They tend to take more time than just running plain PHP code (unless your code is doing something outrageous).
The obvious way to fix this problem is to reduce the number of queries that WordPress needs to perform. And that’s always worthwhile! But it’s not something the modern WordPress server stack can help with.
We might not be able to reduce the number of queries that WordPress makes, but we aren’t out of options either. There are still two ways that the stack can help us optimize the query-result cycle. It can reduce the number of queries made to the database in the first place. And for those queries that make it to the database, it can decrease the time it takes to run them.
These two options are both meant to do the same thing: make PHP wait as little as possible for results from the database, which will make WordPress itself faster.
Stack Elements For The Query-Result Cycle
Let’s look at the different stack elements involved in the query-result cycle. This part of the stack is less complex. But it still involves more than one component — namely, the MySQL database server and the object cache.
MySQL Database Server
A few years ago, a MySQL database server would mean the same thing to everyone. It was a server with the MySQL server installed. But things have changed a lot in recent years.
Various groups weren’t happy with how Oracle was managing the MySQL project. So, each group forked it and created its own version that you could “drop in” instead. The result is that there are now several MySQL database servers.
The new “official” MySQL server is the MariaDB server. It’s the community-developed version of MySQL server. The community plans to maintain full compatibility with the MySQL server project.
Another popular alternative to MySQL is the Percona server. Unlike MariaDB, Percona is more of a branch of MySQL. Its developers aren’t against the MySQL project itself; they just want to focus on improving MySQL’s performance. The MariaDB team later merged some of these performance improvements into the MariaDB project.
At the end of the day, you can pick the one you prefer. There’s no difference in performance between a Percona server and a MariaDB server (for most of us anyhow). They both perform better than MySQL. Percona does, however, maintain closer compatibility with the Oracle project.
What does affect performance is the storage engine that the WordPress database uses. The storage engine controls how the database server manages the data it stores. You can also set a different storage engine per database table; you don’t have to use the same one for the entire database.
A database server has several storage engines. We’re not going to look at all of them. Only two interest us: InnoDB and MyISAM.
By default, WordPress uses the default MySQL database engine. Before MySQL 5.5, that engine was MyISAM. If you run a small WordPress website, then MyISAM is fine. MyISAM runs into performance issues once a website grows in size. At that point, InnoDB is the only choice for a database engine.
The only issue with InnoDB is that it requires some tuning to perform at its best. If you’re running a large database server, you might need to adjust things. Luckily for us, there’s a tool to help with that.
MySQLTuner is a small script that analyzes your database server. It will generate a report and give you tuning recommendations.
Object Cache
The brunt of the work of optimizing the query-result cycle lies with the object cache. The job of the object cache is to store data that is time-consuming to get or generate. As you might guess, database queries are a perfect candidate.
WordPress uses the object cache a lot. Let’s say you use get_option
to get an option from the database. WordPress will only query the database for that option once. It won’t query it again the next time someone needs it.
Instead, WordPress will fetch the query result from the object cache. This is a proactive step that WordPress takes to reduce the number of database queries that it needs to make. But it isn’t a foolproof solution.
While WordPress will do its best to leverage the object cache, a plugin or theme doesn’t have to. If a plugin or theme makes a lot of database queries and doesn’t cache the results, the stack can do nothing about it.
In such cases, most of the database queries will come from WordPress itself. So, you’ll get great mileage out of WordPress’s built-in use of the object cache. That’s why it’s an important element of the modern WordPress server stack.
Now, a problem with the object cache is that it doesn’t persist the data it stores by default. It just stores data in memory while PHP is executing all of the WordPress files. But once the PHP process terminates, all of the data that it stored in memory gets cleared.
This isn’t ideal at all. An object cache could stay valid for a long time, so you don’t want to limit it to a single request. The solution is to use a persistent object cache.
A persistent object cache often comes in the form of a plugin. That plugin would make use of the object-cache.php
drop-in to do its job. This drop-in lets plugin authors change the default behavior of the object cache.
The plugins then connect the object cache to a persistent data store. They do that by replacing the fetch and save functionality of the default object cache. Instead of saving and fetching data to memory, the object cache does it from that store.
Persistent Object Cache Plugins
Nowadays, there are two popular data store options for persistent object caching:
Both of these data stores use RAM for storage, which makes them lightning-fast. In fact, their performance is comparable to that of the default object cache.
The only problem is that they don’t come preinstalled on servers. And neither does their PHP extension (which is optional with Redis). You need to install one before you can use the corresponding WordPress plugin.
Which one should you install? In practice, there isn’t much of a difference between the two for object caching. In the past, the popular option was Memcached. This has changed over the last few years. Redis has added a lot of features that have made it the go-to option for object caching.
Getting Your Own Modern WordPress Server
So, how do you get your own server? The obvious way is to get one from a top-tier WordPress hosting companies. These companies want to stay at the forefront of the WordPress hosting business, which motivates them to adopt the latest breakthroughs and technologies.
But what if you want one without breaking the bank? A couple of tools are available to anyone who would rather do it themselves and pay less for hosting. Let’s look at them.
DebOps For WordPress
DebOps for WordPress is a tool that I built to help anyone build a modern WordPress server. Its mission is to make the modern WordPress server stack available to anyone in the community. That’s why I’m trying to make it as easy to use as possible. You don’t need any system administration knowledge to use it.
DebOps for WordPress configures a server with the following:
- HHVM (until PHP 7 makes it into an official Linux repository)
- MariaDB
- nginx
- Redis
- Varnish
The tool does more than just configure a server with the latest technologies. It also takes care of securing the server for you. This is something that people often overlook when managing their own server.
EasyEngine
EasyEngine is a command-line tool designed to help you set up a WordPress website on a server. The great thing about EasyEngine is its flexibility: You can use it set up almost any combination of server technologies that we’ve looked at so far.
For example, it lets you set up a server with either HHVM or PHP 7. It lets you chose between Memcached and Redis for your persistent data store. And it lets you install administrator tools such as phpMyAdmin.
It also offers a large number of options when it creates a WordPress website. You can tell it to set up a website with an HTTP cache using a plugin or nginx. All of this flexibility is why EasyEngine is such a popular tool.
Trellis
Trellis is a tool developed by Roots. Like DebOps, it configures the server with a specific set of server technologies:
- MariaDB
- Memcached
- nginx
- nginx HTTP cache (optional)
- PHP 7
One thing to know about Trellis is its relationship to Bedrock, another tool built by Roots. Bedrock is a boilerplate for structuring a WordPress website around the “Twelve-Factor App” principles.
The Roots team created Trellis to enable people to configure a server that uses Bedrock-structured WordPress website(s). You can’t use it with a normal WordPress installation, so keep that in mind.
Times Have Changed
As you can see, a WordPress server has a lot more moving parts today! But this doesn’t need to be a cause for despair. It’s not as bad as it looks because you don’t always need to use all of the parts.
That’s why so much of this article discusses how these parts work together. The point is to empower you to make your own decisions. Use this knowledge to decide which parts you need to use and when. In this way, you too will have a fast WordPress website.
This article has first been published on Carl Alexander’s blog.
Front page image credits: Pixabay
Further Reading
- Proper WordPress Filesystem Permissions And Ownerships
- Moving A WordPress Website Without Hassle
- How To Develop WordPress Locally With MAMP
- Do-It-Yourself Caching Methods With WordPress