WordPress is the most popular open-source CMS on the planet that has made the LAMP stack (Linux, Apache, MySQL, and PHP) stacks almost ubiquitous in a cloud hosting environment. This guide covering WordPress server tuning tips caters for the website owners who host their blog or website on a virtual private server (a.k.a. cloud server), not a shared hosting plan. You’ll need root access (SSH) to apply most of the optimizations outlined in this article, especially to their full potential.
If like many other WordPress site owners you started your website on an inexpensive shared host such as Bluehost, it’s a good time to consider moving to a VPS or managed WordPress hosting service as soon as you notice slowdowns. Is your WordPress admin dashboard taking extraordinarily long to load as you add more plugins to your website, or are you concerned about the negative SEO impact of a slow Google SiteSpeed score?
A VPS, usually referring to an unmanaged (or partially managed) VPS is a Linux virtual machine in a datacenter with a minimal image of your operating system of choice, typically Ubuntu Server, Debian, or CentOS. There are typically no hosting applications pre-installed on the VPS. It is up to you, or a system administrator, to install & configure the web server (usually Apache or nginx), PHP and its extensions, and the MySQL database server to get WordPress up & running with a cloud server. Instead of graphical control panels such as cPanel, administration of the server is typically done through a Bash terminal via SSH.
Examples of a managed WordPress hosting services include the paid plans at WordPress.com (not .org), WP Engine, or Kinsta. With these providers, you have to worry about nothing except writing & uploading your content through the WP Admin dashboard. All the other technical things are managed by the webhost, as the name of the service would suggest. But these services have drawbacks. They are typically priced based on the number of monthly pageviews to your website, which means you could see a surprise bill if your website suddenly gets on the front page of Reddit or Hacker News. Also, managed WordPress hosting providers impose technical limitations on the WordPress plugins you may install in their environment. If your theme (especially premium themes) require certain plugins, it may not be compatible with the service you select.
A better alternative to managed WordPress hosting can be to hire an agency such as ourselves to setup & optimize WordPress on a cloud platform such as AWS, DigitalOcean, or Linode. If you already have an existing WordPress website, we can seamlessly migrate it to the VPS of your choice. Although you will pay us a one-time fee to perform the optimizations so your website loads as quickly as possible, the monthly price for VPS hosting is typically lower than managed WP hosting. Once set up, usually WordPress websites require minimal maintenance meaning you will enjoy lower costs than with WP Engine or Kinsta to serve up the same number of pageviews.
Like other subscription services, the price for WP Engine or Kinsta rapidly increase once your needs go beyond their “Starter” plans. The next price tier after $35/mo is $115/mo with WP Engine, after you exceed 25,000 pageviews or 10GB of storage. As anybody with a website packed with plenty of multimedia content would know, it’s super easy to go over 10GB of storage when a modern smartphone takes 16MP+ photos that even when compressed can be many megabytes each.
With a VPS of your own at DigitalOcean or Linode, your WordPress website could get computing resources that are dedicated to you, instead of shared with the managed provider’s other clients. Even though Kinsta themselves built their platform on Google Cloud Platform, you’ll be able to scale far beyond 2 to 4 PHP workers on a DigitalOcean droplet or Linode at the same price point. At $60, which is the price for Kinsta’s “Pro” plan, you could get a Linode with 8GB of guaranteed RAM and 4 dedicated CPU cores. While Kinsta’s platform is LXC/LXD container-based and there are certain efficiencies of scale that they can achieve by spanning their customers’ WordPress instances across their infrastructure, compute resources are so inexpensive nowadays it’s often better to have your own private node you fully control. With a few clicks from the providers’ dashboard, you can temporarily migrate to a more powerful VPS plan then scale back down after a period of high traffic has passed. For users who require even more sophistication, our WordPress consultants can build you a custom infrastructure with load-balancers and auto-scaling groups that will automatically spawn more Apache servers or PHP-FPM workers as memory usage becomes elevated on the primary instances.
Once your WordPress website has been migrated to a VPS, you can use all sorts of tried-and-true tactics to make it load as fast as possible for your audience globally. Some of these are easy for novice or intermediate users to implement on their own (as simple as installing a plug-in), but others require some technical wizardry to test & optimize for the ideal server configuration. There are many parameters that can be tuned to get the most out of each recommendation, which is why we suggest contacting one of our consultants for a site audit and customized action plan to speed up your WP instance.
- Use a CDN such as CloudFlare
Benefit: A content delivery network (CDN) sits between your origin server and your visitors, caching copies of your web pages and/or static assets at PoPs (Points of Presence) operated by the CDN provider, around the world. Once an artifact has been cached, it can be served to subsequent visitors from the PoP with the least latency to their location – shaving precious milliseconds off page load times. Although not always the case, this usually happens to be the geographically most proximate PoP to the visitor.
How it’s done: The webmaster must register for a CDN service such as Amazon CloudFront, CloudFlare, or MaxCDN and modify their DNS records pointing to the CDN service. Sometimes installing a CDN also involves installing a WordPress plug-in to rewrite the URLs for images, CSS, and JS to be pulled from the CDN.
Pitfalls to avoid: Using a CDN can make development more challenging, as changes to your website may not reflect immediately at the edge location, even if you refresh your browser. Luckily, all CDN providers provide a “clear cache” button in their dashboard as a workaround to this. For CDNs such as CloudFlare that require migrating the entire DNS zone to their authoritative name servers, care must be taken to avoid breaking services such as file and email servers.
- Compress images
Benefit: Scaling the dimensions of image files to as close to the actual size they are displayed in the browser viewport reduces data usage, and consequently page load times. Using more optimized image compression algorithms can also help drastically reduce image size with no noticeable loss in picture quality.
How it’s done: Install a WordPress plug-in such as WPMU DEV Smush Image Compression or reSmush.it Image Optimizer. WPMU DEV’s free version can only compress 50 existing images on your website in a single batch, but reSmush.it is donationware that does not impose this limitation. It’s typical to see JPEGs and PNGs compressed by an additional 40-75% after being “smushed” by one of these plugins. The original copies of the images are always preserved safely on your server.
Pitfalls to avoid: Uploading visuals through WordPress’ Media pane will be slower as the compression plug-in must wait for the external API to process the image before saving it in multiple sizes on your server. This does not affect your users, only the editors and administrators of your website.
- Install a caching plugin like WP Super Cache or W3 Total Cache
Benefit: WordPress’ extensibility and amazing ecosystem of plugins can be credited to how posts & pages on WP websites are dynamically generated by PHP and MySQL queries upon page load. At the same time, the computational power required to generate pages “on-the-fly” make WordPress sites load much slower, compared to their static HTML counterparts.
Caching plugins for WordPress temporarily cache the static HTML versions of your WordPress pages in a special folder on your server, so PHP does not need to be called every time a page is requested — reducing the performance penalty of using a CMS such as WordPress.
How it’s done: Install a caching plug-in such as WP Super Cache or W3 Total Cache, two of the most well-known and battle-tested choices available to the WordPress community. WP Super Cache is the one we would recommend to beginners, as they have an “Easy” mode with presets that will deliver a performance boost without a huge risk of breaking your website. If you like to do more advanced tweaking, W3 Total Cache has more robust features that minimize HTML, CSS, JS (tip #7), leverage browser caching (tip #8), database caching (tip #9), and allows you to upload static files directly to a compatible CDN.
Pitfalls to avoid: Much like a CDN cache, using a WordPress caching plug-in can make it tougher to see changes while you make changes to your website. In our experience with WP Super Cache, it automatically flushes the cache when you update a post or page, and if configured properly, avoids caching pages with a query string in the URL which could have dynamic (or user-specific) content. If you use some of the advanced options in WP Super Cache or W3 Total Cache that require modifying .htaccess files (e.g. enabling mod_rewrite mode in WP Super Cache), remember to make backups of your server configuration files prior to applying the changes.
- Upgrade to PHP 7.1, 7.2, or 7.3 (the newer the better)
Benefit: Many WordPress websites in the wild are still running antiquated versions of PHP that have reached end-of-life, most commonly PHP 5.6. This has become such a huge concern for the open-source maintainers that the latest versions of WP will actually warn you in the dashboard if you are running a PHP version prior to 7.0. Deprecated versions of PHP do not perform nearly as well as the latest versions and may include unpatched security vulnerabilities, making your WordPress site more likely to get compromised by a hacker.
According to benchmarks run by Kinsta, PHP 7.3 can handle 3x as many requests per second as PHP 5.6, making a world of difference when it comes to page load times. Another study by Savvii revealed that page load times were reduced by 72% between PHP 5.6 and PHP 7.3, and Time to First Byte (TTFB) was 44% faster between PHP 7.2 and 7.3.
How it’s done: It depends how PHP is currently installed on your WordPress VPS. On most servers, PHP is usually installed through a custom repository. For example, CentOS users might add the IUS or Remi repos. To check which PHP modules are currently installed on your server, do a rpm -qa | grep php or the equivalent on your distribution. Usually the version of PHP is denoted in the naming convention of the packages in the repo. For example, all packages including extensions for PHP 7.2 might be labelled php72-php- and PHP 7.3 would be labelled php73-php- and so on so forth. To upgrade PHP on your server using a custom repo, you simply remove the old packages and install the new ones.
Pitfalls to avoid: Only use custom repos that have a good reputation in the community; unknown third-party repos may contain malicious code. Carefully take note of what PHP extensions your applications, including WordPress and others require, and be sure to install their counterpart in the new PHP version. When upgrading, the php.ini file may get overwritten with the default values. If this happens, you must reapply any edits you have ever made to the PHP config file. Take special care if other applications besides WordPress are running on your server. If those apps don’t support the latest version of PHP, you could break them by upgrading your PHP version. One way around this is to install multiple versions of PHP on the same server.
- If using Apache, make the switch from mod_php to php-fpm
Benefit: On Apache servers, PHP runs as an Apache module in a mode called mod_php. It is not a distinct process separate from Apache, with the consequence of being non-thread safe. As a result, Apache can only run with the prefork Multi-Processing-Module (MPM). prefork is far less efficient than the worker MPM, which can spawn multiple worker processes to handle incoming requests.
If PHP is managed by a separate process, known as the FastCGI Process Manager (FPM), it becomes thread-safe so Apache can be configured with the worker MPM. In FastCGI mode, Apache will proxy any request for a PHP page to the PHP-FPM service over the FastCGI protocol, and serve whatever it receives as a response to the user. In essence, PHP becomes a separate microservice which although typically residing on localhost can be scaled out to another server, making it much more aligned with cloud-native design patterns than mod_php.
How it’s done: Install the php-fpm packages corresponding to your PHP version, enable and start the php-fpm service. Modify your Apache VirtualHosts to proxy PHP requests over to FastCGI which runs on port 9000 by default, and update httpd.conf to use the worker instead of the prefork MPM.
Pitfalls to avoid: .htaccess files are only taken into consideration by PHP in mod_php mode. Any directives in .htaccess, particularly concerning permalinks for WordPress, should be moved to .user.ini in the respective application folders. Backup your Apache configuration files and test any changes using the apachectl -t command (look for “Syntax OK” message) before committing any changes by restarting the Apache service.
- Disable unnecessary plugins, especially ones that load external resources
How it’s done: Deactivate & uninstall any unnecessary plugins that are not required by your theme’s author. Sometimes a theme will recommend you install plugins such as Revolution Slider, which you can safely remove if you are not using sliders. Deactivating a plug-in does not usually delete the custom tables/rows it has added to your database, but deleting it through the Plugins Manager will purge any data to keep your database clean & compact.
Pitfalls to avoid: Removing (or even deactivating) some plugins can cause you to lose configuration data that can be critical to the look & feel of your website. It can be wise to go to the Settings for the plugin to check if the plugin preferences can be exported, in case you need to reinstall it.
How it’s done: Minification can be done at the CDN level such as CloudFlare, but more commonly with a WordPress plugin such as Fast Velocity Minify or Autoptimize. If also using a caching plugin, we recommend going with W3 Total Cache which has a minify feature baked into it, to avoid any potential incompatibilities between plugins. Lazy loading might be an option that’s available in your theme’s dashboard (check there first to avoid additional overhead to your WP site), but a multitude of Lazy Loading plugins are also available.
Pitfalls to avoid: No major pitfalls
- Leverage Cache-Control headers for static resources (HTML/CSS/JS and Images)
Benefit: Did you ever notice how some web pages are speedier the second time you visit them? That’s probably thanks to your browser cache, as the webpage (or parts of it) has been temporarily stored in your browser’s cache directory on your hard drive. Cache-Control expires headers are how webmasters & web developers tell your browser how long an asset should stored in the cache, before it’s purged, and fetched anew from the remote server.
How it’s done: Browsers should be instructed to cache static assets such as HTML/CSS/JS and images that rarely change as long as possible. An appropriate setting could be days, or even weeks for scripts such as Google Analytics (analytics.js) or visual elements such as a header/footer backgrounds and logos. This reduces bandwidth usage, and makes the user’s experience snappier as they navigate through your optimized website. W3 Total Cache is the best WordPress plugin for configuring cache expiry times for static assets.
CDNs such as CloudFlare have an option to inject a Cache-Control expires header for any static elements that do not have a cache lifetime specified in the webpage code itself. Any cache lifetimes set up directly in WordPress will always take precedence if they are longer than the CDN setting. Effectively, the CloudFlare setting specifies a minimum cache lifetime across the entire website.
Pitfalls to avoid: Set appropriate cache lifetimes based on how often you expect an artifact to be updated by its developers. Excessively long lifetimes can lead to broken page layouts since outdated resources in a visitor’s browser cache are not being purged. Although one can always override the local browser cache by hitting “Refresh” in their browser, the visitor should not be expected to do so.
- Set up an object cache such as APCu, Memcached, or Redis
Benefit: An object cache (a.k.a. database cache) accelerates WordPress by reducing the number of queries that PHP has to make to the MySQL server, by caching the results of those queries in a data store such as APCu, Memcached, or Redis. It’s far too technical for this guide to get into the minute differences between each one, but they all practically work by using RAM to cache frequently used database queries, rather than having them executed repeatedly against the database server.
How it’s done: This will be covered in a future article.
Pitfalls to avoid: Implementing an object cache is only recommended for WordPress websites with very dynamic (constantly changing) content. It can have the opposite of the intended effect, making performance worse on most WP websites that are fairly static in nature.