High Load WordPress Background
Banner

Optimizing WordPress site for High Load and High Speed

In this section:

What is considered a high load?

This might be a new topic for some of the readers, while others might already be familiar with it, so lets start with some introduction, so everyone can catch up.

Many site owners have a decent amount of customers, but that doesn’t necessary mean they have a high load on their site.

If customers don’t visit the site all together at the same time, a site might have 10000 customers daily, but a day has 24 hours, which is 1440 minutes. So if an average customer spends around a minute on a website, you might still only have less than 10 customers per minute, which is not a high load and many sites can handle that much with an average hosting.

However, sometimes you promote special offers or bring many new customers from an advertisement campaign, and they might start visiting your site with a higher rate – from 10 customers a minute to 1000 a minute and you better be prepared for that, because otherwise they will see an unresponsive site and waste your marketing efforts.

Also if you are actively blogging, your articles might eventually rank high in search results and lead to unexpectedly large amount of visitors in a short period of time.

Besides, it is important to note, that every user might send multiple requests to your server – visiting pages, loading pagination and submitting forms – each of those actions will be a separate request, so as related resources used on those pages – images, styles and scripts.

Considering that, high load is usually measured in amount of requests, not in amount of visitors.

With that introduction to the topic, lets see how we can manage a large amount of requests while also keeping website fast.

Website code optimizations

The first line of defense to have a website that can handle many requests is to optimize your code and apply various content serving best practices.

How? By applying these two ideas:

  1. Improve processing speed for every individual request and/or memory consumption
  2. Reduce amount of requests reaching your server through caching techniques

Request CPU and memory usage optimizations

  • Static requests. Most of the requests are quite simple and fast. The most fast requests are static content – CSS and JS files, images are served very fast usually, but they still make a load on your server, there are not many efficient techniques to optimize those requests and you should just cache them in most cases, see the next section for information.
  • Dynamic requests. Then, there are requests with some computing going on without SQL queries to database, those are slower, and their speed might vary, depending on the complexity of the computation and the amount of code to compute. On simple sites those requests are usually also fast enough, but some sites can have a bottleneck here, if they are rendering large DOM server-side. Some language functions are less effective and take more time, so if you are using functions like preg_match() a lot – page generation might eventually become slow.
    There a number of slow native functions in PHP language, but you might also have a lot of custom slow functions in your project, that ineffectively use system resources. Profiling tools like XDebug extension for PHP can show you such slow executions, and for that purpose I included its installlation in PHP container that is used by WP BOX.
  • Dynamic requests with SQL. These might be the heaviest ones, but even among those, some are still fast. Like fetching current page from database or a list of latest pages or a list of pages by category – in WordPress those default queries are fast enough, but if you create your own filter or ways to get data from database, eventually you might get an ineffective SQL as well (or install it with a third-party plugin or theme). You can see how SQL performs on your site if you install a Query Monitor plugin for debugging even without special knowledge.

Take a look at the screenshot below, this is a query for https://wp-box.org homepage content, you can see the exact SQL in the first column, and processing time in the last column.

Query Monitor fast query execution

But, on the other hand, some queries are slow by default. And you should try avoid using them or look for a hacky solution.Some business logic requirements might leave you no choice, and sometimes you have to use slow queries.

Examples you should avoid:

  • Querying random posts, while it might seem ok at first, it will get slower and slower as your database grows.
  • Querying posts by metadata columns. WordPress database is designed in a way, that makes those queries slow. And they also get slower with the database size.
  • Querying with non-standard methods. Use WP_Query and $wpdb for custom requests, because they are better covered with caching out of the box.
  • Querying by other columns, that are not indexed. Only indexed columns are fast enough.

Examples, where slow queries usually appear:

  • Search, which is slow because it often looks up for partial strings inside non-indexable columns.
  • Product and post filters built on meta fields instead of taxonomy terms.
  • Unlimited, non-paginated queries for many results at once. This might happen both on a shop page and during generation of custom XML feeds with data.
  • Random posts or product suggestions after the main one. Better show posts from related categories or taxonomies, previous/next or manually selected by ids.
  • Other custom complex queries extensively filled with joins and custom logic.

For those there might be no universal advice on speeding them up, its best to identify those bottlenecks and engineer a custom solution, if for some reason you cannot avoid using them. Besides you should consider caching and lazy loading some queries – e.g. you don’t need to perform a search query with page load, you can get the input from search form, show a nice spinner and take your time to load results. This last particular improvement is not a high load technique though, just a barely related UX advice.

Reducing amount of requests with CDN and caching

This is a very important optimization to reduce load on your server, however you should note that it only applies to GET requests – page loads, template loads, paginated results loads and other content, where you don’t process user input or show user-specific content.

Best practice would be to return content as early as possible, without passing request.to the next layer, so the layers are:

  • Browser Cache. You can put some static resources there, like images, styles, frontend scripts. Make sure to save every resource with unique version parameter, e.g. styles.css?v=1. Applying versions to the end of file is your opportunity to reset cache when needed. Basically when you deploy a new release, you change the file version in the references, and all users with a resource cached in their browser, will redownload a newer version. Besides, resources cached in browser are downloaded instantly!
  • CDN Cache. CDNs not only are a caching layer, they are also closer to the user and result in faster content downloading. So if for some reason you don’t want to store some static content in browser cache, you should store it in CDN with reasonable cache time. Usually I set up two different rules, one rule for static content and another for requests going to specific routes, that I know can be cached from 1 minute to 1 day. All other requests are going to your server. Also CDN can serve as Load Balancer for your own servers.
  • Nginx. Im using it as a web server, it can also be a load balancer. So Nginx will handle all requests, and you usually setup it to return static right away and pass requests to PHP scripts to the PHP container / local PHP service. Note, that every time when cache expires on the above mentioned layers, a request will reach your server to fetch content again.
  • PHP & Redis. At this layer you can cache both results of the queries and also rendered templates. With WP BOX installation pipeline a container for Redis and PHP is created, and a plugin is installed that makes it available for use inside WordPress without any modifications, because WordPress caches queries by design, if a caching service is available. In some case you might put entire header or footer templates into cache for a faster rendering. To save data into Redis you can use free plugin for WordPress – Redis Object Cache.
  • Database. With any database it’s important to write effective SQL, I already explained above some principles. Here I just want to repeat – you have to use indexes to make your queries fast and be able to withstand a decent load on your site.

What cannot be cached?

  • You can’t cache entire page if some of its parts are user-specific. For example if you show a user profile image based on a current user ID. Such dynamic content can be loaded from REST API endpoint or with Ajax request, not during the initial page load, if you want to cache the page.
  • Another example is a template part, you might want to cache entire header, so it is taken from cache with every other page load. However, a language switcher in header in that case should also be loaded separately.
  • If you have user-generated content, like comments, likes, various counters of shares and other interactive stuff, you can’t cache the page as well, until you move loading of those elements into frontend scripts.
  • Captchas. Some simple captchas are server-side generated and are not compatible with cache, use ReCaptcha or any other modern frontend captchas.
  • If you use wp_is_mobile(), setting cookies or performing any updates to database during page load, you cannot cache the page as well. Engineer a different solution.
  • Nonce. When using nonces, they are rendered into DOM structure, and if you cache the page, your frontend scripts will eventually use cached version of the nonce.

Database Replication & Load Balancing

Database replication is a more advanced technique, which many projects would never need, however it comes useful in three situations:

  • You have requests to origin server that should be processed faster
  • You have growing amount of requests in general, that are still reaching your server even with caching
  • You are implementing blue green releases, where production is replaced with staging with 0 downtime

First case is more known with applications like Video Games where miliseconds matter, and if you have a server in USA, but request is coming by user from Asia – every request will have a distance delay, even on well-optimized site.
CDN partly covers these needs, but only for GET requests that can be cached, if you want your uncacheable requests to be really quick, you will need to have a server close to user location. This often is a requirement for E-Commerce and FinTech websites.

This also applies for the second case, you might need additional servers just to handle regional loads, for a website with replicated database amongst all instances.

These cases might be implemented with and without actual load balancing.

WP BOX and High Load

Author of this project has faced problems discussed in this article and is developing WP BOX with potential high load in mind.

It might be not the most effective ever solution, because Im trying to keep a balance between effectiveness and ease of usability with reasonable support costs, thus WP BOX is designed to be universal. Code that is written for the project is optimized to be reasonably effective, without sacrificing comfort working with a site on a daily basis.

This website is deployed on Google Compute Engine and using Cloudflare CDN, but it will perform similarly on any other VPS or your local computer.