Professional Blogging: Comprehensive Guide to Optimizing WordPress

Share:

Running high traffic sites means WordPress optimization is crucial. I spend a lot of time reading, thinking, and testing configurations to constantly improve the performance of my WordPress blog, as well as the server load. When sites are getting over 100,000 visitors per month, every millisecond counts. Whether you are running a blog, enterprise site, or local business, ensuring your visitors and customers have a fast speed can make all the difference between someone who clicks away and someone who stays for a while.

This post is formatted in a way which starts with the lowest hanging fruit and gets harder and more complex the farther down you read. Optimizations are also organized in a cascading order, where some will not help nearly as much if you skip some of the steps. Begin your WordPress optimization from the top and begin measuring results!

This step-by-step optimization guide has been tested on two different server environments. One is a VPS which receives over 200,000 visitors per month. The second is a dedicated server which receives over 800,000 visitors per day. Countless benchmarks and improvements have gone into each server, improving its speed and reliability.

Last Updated: November 27, 2017

Disclaimer: This post includes affiliate links for some of the products mentioned, however, I am not being compensated to endorse any of the products. 

Disclosure on the results of this blog: I run ads on this site, which will always skew results and make the site load much more slowly. If you run an ad-free site, you will experience enormous benefits from using this optimization guide.

optimize wordpress pingdom resultsWordPress Web Hosting

The first step in WordPress optimization is choosing a good web host. As a web developer, I’ve used and been exposed to a huge variety of web hosts. I’m ultra-picky and want the very best available for my budget and needs. I use iWFHosting. Their solutions include server management, which is awesome on multiple levels. I can’t say enough good things about this company, their integrity, and 24/7 support.

A lot of people running WordPress websites are going to be on what’s called “Shared Hosting” plans. While these are not inherently bad, a lot of times web hosts overload the amount of customers on a single server, causing a slowdown for everyone.

With the amount of  traffic my blog brings in, I’m beyond the shared hosting spectrum. I run a VPS, or virtual private server. Running a VPS allows me to quickly scale on necessary, providing the necessary power and bandwidth to run a speedy website.

Current stats on the VPS which is receiving over 200,000 visitors per month:

iWF VPS – CentOS 7.x KVM, 200GB SSD, 8GB RAM, 2.2 GHz 4 cores Virtual. $70/mo.

Current stats on the Dedicated which is receiving over 800,000 visitors per day:

iWF Dedicated – CentOS 7.x, 250GB SSD, 32GB RAM, Xeon E3-1270v3 (4 Cores). $160/mo.

As you can see, these aren’t incredibly expensive solutions to purchase. If you’re running your own business or are using a blog to provide income like me, you should at the very least be on a VPS. Nearly 0 downtime and an ultra-fast server means customers are much more likely to spend time on your site and purchase whatever you are selling.

Beyond Hosting – A CDN for WordPress

With a good web host selected, the next step is getting a content delivery network, or CDN setup. I ended up with MaxCDN for several reasons. The first being a lot of plugins support integration with their API. The second being their support and speed were excellent. I’ve bugged them multiple times with silly questions and their expertise has always proved worthwhile. With a Pull Zone setup, these are the ideal settings I’ve found for a WordPress site running SSL/HTTPS.

WordPress Optimization

With a good server setup in place, now we can move on to actual WordPress optimization. The first step is getting some plugins in place to do some automated tasks. Then, we’re going to analyze what the WordPress site is currently loading, and then install a few more plugins and optimize the theme.

WordPress Plugins to Help Auto Optimize

WP Rocket
This is one of the best automatic non-configuration-required caching plugins I’ve found and used. In addition to doing everything you need to do with minimal configuration on your end, it also plays VERY nicely with the server setup we’ll get into later on, with some great integrations.

This includes CDN support for ALL networks – not just for 1 or 2 CDNS like many other plugins out there.

The plugin also minifies CSS and javascript, which will allow you to dump any extra minify plugins.

Oh, did I mention it includes database cleaning tools for post revisions and transients? Go ahead and dump your extra plugins for that, too.

This plugin is a premium plugin, at $40 per site, and is oh so worth it.

wordpress optimization wp rocket settings

Memcached Redux

This will tie your WordPress website into your PHP extension Memcached, listed further below in the steps. It works alongside WP Rocket, so don’t worry about running multiple caching plugins.

WP Rocket uses opcache, Memcached Redux uses memcached. Two different types of cache, both are needed.

wordpress optimization memcached redux

EWWW Image Optimizer
Optimizes all of your images and PDFs into the smallest size possible without sacrificing quality. This means your assets will load much faster. The only settings I usually touch on this plugin are Deferred Optimization and I use a bit more intense optimization levels. This is so I can work on posts and upload images faster, and I know when my wp_cron (will get to this soon) runs every hour, the images will be optimized.

wordpress optimization ewww image optimizer

Heartbeat Control
WordPress has an API that autosaves drafts which by default goes every 15 seconds. This plugin lets you change the frequency to every 60 seconds. There is an option to disable where it runs, but I kept it everywhere so future plugins or features aren’t affected. I could totally see myself spending an hour trying to figure out why a plugin doesn’t work, only to realize its a setting here.

wordpress optimization heartbeat control

Really Simple SSL

I like to use this plugin, even if I already setup the site to use https. This automatically sets up all the proper redirects and makes sure rogue images and scripts use the proper protocol.

wordpress optimization really simple ssl settings

Handy tip for Really Simple SSL: It adds a snippet to htaccess for the redirects. If you ever need to remove them due to certificate errors or another issue, this code will mass remove the code from every htaccess on the server without touching anything else. Credit to iWF Hosting:

perl -0777 -i -pe ‘s/# BEGIN rlrssslReallySimpleSSL.+# END rlrssslReallySimpleSSL//sig’ */public_html/.htaccess

 

Analyzing Your WordPress Performance

Head over to either GTMetrix or Pingdom. Or, do both and see how the different companies analyze your site, cause you’re just as particular as me.

wordpress optimization analysis

This is what a typical graph will look like as I optimize a WordPress site, testing as I go. I was already familiar with my own site, so the process didn’t take as long, but it generally takes anywhere from 2-6 hours to optimize a WordPress site before even getting to the server itself.

Going off the GTMetrix waterfall view or the Pingdom File requests view, the first thing I look for is how many fonts are being loaded. A lot of times themes or plugins load multiple themes. Your site should have a consistent brand and look, so this will accomplish several things. Removing extra fonts from loading will improve your branding, as well as reduce the number of requests for your site to load.

Next, look for any 404 errors that are popping up. Go and find out what is missing, and fix those. For example, in testing this site, I realized I had a 404 image that was causing a huge slowdown. Oops.

I like to check out the 200 response code request. This is going to be the first one in the list and the most crucial. This is also where caching will shave off a lot of time.

Keep analyzing the waterfall as you go, and take care of different items that seem out of place to you. Try playing with BWPM to get to the ideal configuration.

Remove, minify, or modify one item at a time, flush your cache, and retest. If something breaks, its easy to undo what you just changed, versus guessing over multiple items. This is the most tedious of the entire process and takes a lot of close scrutiny. You’ll also have a much better understanding of how your website works and dependencies when you’re done.

High Traffic Websites and wp-cron.php

Every time a visitor goes to your site or loads a page, wp-cron.php is triggered. This is WordPress checking to see if it needs to do anything. This includes checking for updates, scheduled posts, email notifications, or a number of other tasks. When your website is getting several thousand or more hits per day, this uses up resources unnecessarily. Disabling the default wp-cron.php will be a lifesaver.

Open wp-config.php and add the below code. I usually place new code near the debug function.

define(‘DISABLE_WP_CRON’, ‘true’);

optimize wordpress wp_cron

Now, login to cPanel and setup a Cron Job to hit wp-cron.php every hour. Change the username to your cPanel username and make sure the path is correct, especially if you’re on a subdomain or subdirectory.

cd /home/CPANELUSERNAME/public_html; php -q wp-cron.php

optimize wordpress cpanel wp-cron php

Database Bloat and Revision Control

Every time you save a post, WordPress saves a version of that post in the database. These are all accessed via the Revisions interface. WordPress will keep saving every version indefinitely. I got up to 48 revisions on this post alone in writing the initial spec. Figuring out an appropriate number of revisions for your style and setting that will reduce the size of the database. I personally set mine to 5 via wp-config.php:

define(‘WP_POST_REVISIONS’, 5);

Increase WordPress Memory

The default WordPress memory limit is pretty low. 40MB, as it just so happens. This is not enough for many many environments. Why is it set so low by default? This is so WordPress can run lean and mean on many servers, with users not required to purchase high end equipment. However, if you’re reading this, you’ve already upgraded to a VPS or dedicated server and are ready to increase the memory. If you leave well enough alone, you’ll encounter quite a few errors and timeouts on the backend. 128M is a good starting point, assuming your server has more than 4GB of ram. Add this cod to your wp-config.php:

define( ‘WP_MEMORY_LIMIT’, ‘128M’ );

Prefetch Common Domains

Prefetching is similar to caching. You are instructing the browser to lookup DNS requests before they are needed. This allows pages to load much quicker later on. Add these to the header of your theme. Either use your theme customizations or a plugin to make this happen. I use WP Rocket to integrate with my other caching optimizations. I also include my CDN domain, which is not listed.

<link rel=”dns-prefetch” href=”//fonts.googleapis.com”>
<link rel=”dns-prefetch” href=”//ajax.googleapis.com”>
<link rel=”dns-prefetch” href=”//www.google-analytics.com”>
<link rel=”dns-prefetch” href=”//apis.google.com”>
<link rel=”dns-prefetch” href=”//accounts.google.com”>
<link rel=”dns-prefetch” href=”//fonts.googleapis.com”>
<link rel=”dns-prefetch” href=”//pixel.wp.com”>

Miscellaneous WordPress Notes

Too many plugins equals a slow site – This is a false myth. I currently have 50 active plugins on this site, and another 20 or so which are inactive. I get load times in under 3 seconds, and often under 1 second.

wc-ajax=get_refreshed_fragments – This is something which will show up as a slowdown if you’re running WooCommerce or haven’t installed the Heartbeat Control plugin. Its probably a bad idea to disable completely. I run WooCommerce on this site, and the only way to speed it up significantly is to disable the ajax-cart option here: /wp-admin/admin.php?page=wc-settings&tab=products&section=display

Remove extra fonts being loaded – If you can’t find where fonts are being loaded, or if the theme is loading extra fonts on its own, removing them via BWPM is the way to go.

Themeforest themes are slow – Would you be surprised to learn I am using X and a child theme? Sure, there are bad themes out there, but it isn’t always the theme causing slowdowns.

cPanel WordPress Optimization

Optimize Website – This generically named setting is where you can enable compression/gzip of your content.

wordpress optimization cpanel compress gzip mod_deflate

For the more advanced readers – Zlib for compression is another good option when configuring WHM.

While not directly related to WordPress speed, its a good idea to make sure your DKIM and SPF records under Authentication are valid. These records help fight spam and let’s other mail servers know that your e-mail is authentic.

wordpress optimization cpanel dkim spf

Advanced WordPress Optimization

Alright, time to take a break, make a fresh brew of coffee, and get ready to dive into the heavier and more advanced areas of getting the most performance out of your WordPress website.

advanced wordpress optimization

Disclaimer and notice of imminent danger: Modifying settings in WHM could very well render your enter server useless. This guide includes items which can break stuff. If you feel unsure of a setting, or want a better understanding, ask your web host support or search for more information. I am only providing a very superficial overview on many of the following items. WHM supports Dry Runs of some of these settings, particularly EasyApache – take advantage of this, even if it takes you longer.

My current setup is a VPS with 8G Ram, 4 Xeon CPU cores, SSD drive, SolusVM, CentOS 6.8, and WHM56. If your setup is radically different, you may encounter problems. Feel free to ask your web host if these settings are safe.

WHM WordPress Optimization

There is a lot to adjust within WHM. I’m going to go down the menu and just list changed settings related to WordPress performance. There are plenty of other settings you may want to change for security, or based off of a specific website environment.

If there is another setting within WHM I missed which has impacted your WordPress performance, comment with an explanation and let me know to add it. Most of the heavy lifting was done prior to this point, and now that we are in WHM/PHP configurations, I’d love to have some input from others on further performance tuning.

Again, only settings which have been changed from their default and related to WordPress optimization are listed here.

Engintron and Nginx

Engintron coupled with Nginx works as a reverse caching proxy in front of Apache, enabling micro-caching, improves performance, reduced CPU/RAM load, and integrates with WP Rocket. You can read all about it by visiting the Engintron github page.

optimize wordpress engintron nginx

Install via SSH with one easy command:

cd /; rm -f engintron.sh; wget –no-check-certificate https://raw.githubusercontent.com/engintron/engintron/master/engintron.sh; bash engintron.sh install

You can manage Engintron from within WHM.

Change Nameserver to PowerDNS

By default, WHM installs with the BIND nameserver. The nameserver is what points a domain name to the correct IP. PowerDNS is much faster and will decrease your TTFB – time to first byte in the DNS and SSL portions. I went from around 300ms to under 100ms in multiple environments. There are specific situations in which you need to use BIND – in which case you’d know what these are and will ignore this section.

To change nameservers, login to WHM, navigate to Nameserver Selection, select PowerDNS, and click Save. Wait for the window to indicate the process is complete and away you go!

wordpress optimization nameserver powerdnsInstall APCu

APCu caches only data store. This is not to be confused with APC, which cached opcode and data. OPcache has taken over the opcode cache, making APC necessary – for now. Using both together, with the appropriate plugins is highly recommended. APCu reduces the amount of complex operations WordPress needs to perform on a page request and reduce database access.

From WHM, navigate to Module Installers and select PHP Pecl. Make sure the dropdown for PHP 7.2 is selected, or the PHP version you are using. Search for apcu and hit go. Click on Install next to apcu.

After the install, add this code to your php.ini file:

[apcu]
apc.enabled = 1
apc.shm_size = 128M

After making changes to your php.ini, head over to the Engintron Control Panel and restart Apache ands Nginx to make your changes live.

wordpress optimization apcuWHM Tweak Settings

Compression
gzip compression level: 6
pigz processes: 4
Kb per chunk: 128

Mail
Initial default/catch-all: Fail
Email delivery retry time: 60m
Enable BoxTrapper spam trap: Off
Enable Mailman mailing lists: Off

Stats Programs
Enable Analog stats: Off
Enable Webalizer stats: Off

This leaves you with Awstats still on and enabled. You should have a specific reason for multiple logging programs to be running. You might also be using something like Google Analytics or Jetpack stats.

System
File upload required free space: 128MB

Apache Configuration

Global Configuration

StartServers 5
MinSpareServers 5
MaxSpareServers 10
ServerLimit 500
MaxRequestWorkers 500 (this option was previously called MaxClients in Apache v2.2)
MaxRequestsPerChild 4000
Timeout 20

Piped Log Configuration:
Enable Piped Apache Logs

EasyApache 4 and PHP7 Configuration

Below is my current configuration. I did not include versions for security reasons and its just extra clutter to scroll through. I’m probably using whatever is listed as the latest. Included is http2. Some of these modules and extensions are required to make full use of the WordPress plugins listed earlier.

Apache MPM
mod_mpm_worker

Apache Modules
mod_bwlimited
mod_cache
mod_cache_disk
mod_cgid
mod_cloudflare
mod_deflate
mod_expires
mod_file_cache
mod_headers
mod_http2
mod_mpm_worker
mod_proxy
mod_proxy_fcgi
mod_proxy_http
mod_proxy_wstunnel
mod_remoteip
mod_security2
mod_ssl
mod_suexec
mod_suphp
mod_unique_id

PHP – At this point, there is no reason to keep 5.x version of PHP on your server. I am using PHP 7.2 at this point, but have older versions in case I need to downgrade for some odd reason.

php70
php71
php72

PHP Extensions
php72-build
php70-libc-client
php72-pear
php72-php-bcmath
php72-php-bz2
php72-php-calendar
php72-php-cli
php72-php-common
php72-php-curl
php72-php-dba
php72-php-devel
php72-php-enchant
php72-php-exif
php72-php-fileinfo
php72-php-fpm
php72-php-ftp
php72-php-gd
php72-php-gettext
php72-php-gmp
php72-php-iconv
php72-php-imap
php72-php-intl
php72-php-ldap
php72-php-litespeed
php72-php-mbstring
php72-php-mysqlnd
php72-php-odbc
php72-php-opcache
php72-php-pdo
php72-php-pgsql
php72-php-posix
php72-php-process
php72-php-pspell
php72-php-snmp
php72-php-soap
php72-php-sockets
php72-php-tidy
php72-php-xml
php72-php-xmlrpc
php72-php-zip
php72-runtime

Plus a similar config for 7.0 and 7.1.

PHP Configuration / MultiPHP INI Editor

Basic Mode

max_execution_time: 180
max_input_time: 60
max_input_vars: 3000
upload_max_filesize: 32M

Editor Mode

; Opcache Tuning
opcache.revalidate_freq=0
opcache.validate_timestamps=0 (comment this out dev environment)
opcache.max_accelerated_files=7963
opcache.memory_consumption=192
opcache.interned_strings_buffer=16
opcache.fast_shutdown=1

Random Notes

To delete all old php log files, enter this into your SSH terminal:

find . -type f -name ‘*.php.error.log’ -exec rm {} \;

MySQL / MariaDB Optimization

If you’re like a lot of folks out there, you’re running MySQL 5.6 within WHM. Time to upgrade to MariaDB! MariaDB is a fork of MySQL, which means you can switch back and forth without issue. Although, once you go to MariaDB, you won’t want to go back to MySQL! MariaDB was acquired by Oracle in 2010, and one of its original developer wasn’t happy with how it was being developed, and thus forked MariaDB. So why switch? Faster performance. Anywhere from 5% to 60% faster, depending on what you’re doing. Better security. Faster updates. It goes on.

MySQLTuner

MySQLTuner is a Perl script which checks your current MySQL configuration and makes recommendations to increase performance and stability. For best results, run the script after the server has been operating for a few days or more.  The script does not make any changes, but suggests changes for you to make.

To install and run, SSH and run these commands:

wget http://mysqltuner.pl/ -O mysqltuner.pl
wget https://raw.githubusercontent.com/major/MySQLTuner-perl/master/basic_passwords.txt -O basic_passwords.txt
wget https://raw.githubusercontent.com/major/MySQLTuner-perl/master/vulnerabilities.csv -O vulnerabilities.csv
perl mysqltuner.pl

WordPress Database Tables Optimization

Hop into phpMyadmin, backup your entire database. While you’re downloading the backup, look at the scrollbar to the right. Look how far you’ve come. And now I’m going to reveal an incredibly awesome tip.

select your wp_options table, and run this query:

ALTER TABLE `wp_options` ADD INDEX (`autoload`);

This little query has the power to dramatically speed up your website. Especially if your plugins or theme utilizes transients. Are you running WooCommerce? This is a must. Essentially, adding autoload to the options table drastically reduces the amount of rows called upon. Example: 553,749 to 711 rows in one such optimization. You’ll notice a lot of speed improvement in the backend and dashboard of WordPress as well.

Convert Database Engine from MyISAM to InnoDB

If you installed WordPress prior to any of these optimizations there is a very good chance your database is using MyISAM. InnoDB is a database engine which offers signifigant performance gains. Essentially, MyISAM locks the entire table to make a change, while InnoDB locks just a row. When you have a larger WordPress site, the rows in the database can easily reach millions. Imagine every time someone makes a comment, you make a change, or some other change occurs. The entire database table locks and waits. With high traffic, this is a great way to crash the site.

Login to phpMyAdmin. Select the database you want to change the engine for. Paste this into the SQL tab:

SELECT CONCAT(‘ALTER TABLE ‘, TABLE_SCHEMA, ‘.’, TABLE_NAME, ‘ engine=InnoDB;’)
FROM information_schema.TABLES WHERE ENGINE = ‘MyISAM’;

wordpress optimization myisam innodb

You’re going to get a list of queries, most likely with cut off text. Click the +options button and select Full texts, then click the Go button.

Copy everything under the table row header, starting with ALTER TABLE etc…) . Click the CQL tab and past in the list of queries. Hit Go.

You can also do a manual conversion using just your mouse. From within phpMyAdmin, click on the individual table name, click the Operations tab, select the Storage Engine dropdown, select InnoDB, click Go. Repeat for each table.

Congratulations, you’re now using the InnoDB engine!

Apache Optimizations

Apache Tuning with Apache2Buddy

Apache2Buddy is a perl script which reviews your Apache configuration and makes suggestions based on the Apache process memory and RAM, focusing on the MaxClients directive.

Install and run the script with this command:

curl -sL https://raw.githubusercontent.com/richardforth/apache2buddy/master/apache2buddy.pl | perl

Before making any suggested changes, backup your configuration with this command:

cp /etc/httpd/conf/httpd.config ~/httpd.conf.backup

FastCGI Optimization

You are now using PHP-FPM – there is no need to worry about FastCGI or optimizing it at all.

fin.

Whew! You made it to the end. Please comment if:

  • This helped you in any way – post screenshots of your results from Pingdom or Gtmetrix!
  • I missed something
  • You have a better config to share

Sources

 


Share:
mk

4 Comments
    1. Its oh so worth it and you’ll be much happier when it is all said and done! iWF is a great hosting company with excellent service, for hosting your WordPress site.

  1. Great Article, my only concern is with InnoDB under MariaDB the “ALTER TABLE `wp_options` ADD INDEX (`autoload`);” does not work and throw an error : Table storage engine ‘InnoDB’ does not support the create option ‘TRANSACTIONAL=1’

    Any idea why ?

Leave a Reply

Your email address will not be published.