Wordpress
- knowledgediary4min
- Mar 2, 2020
- 7 min read
Updated: Mar 3, 2020
WordPress Optimization
When to Use This
This is for use when a customer has indicated that a specific WordPress installation is slow. After ruling out issues like a hardware node issue, then proceed with the following optimizations.
We need WordPress admin credentials (and URL) to make these changes. Also prior to making any of these changes, check with the customer. Also please understand that while these changes will work in 95% of the WordPress installations that you will find, it's important to test the sites after making any modifications. Also keep in mind that the following are small changes to eek out slightly better performance from a WordPress site. If they are having performance issues and prior to spending time optimizing the things below look for things like OOMs, disk I/O and CPU utilization in Grove >> Monitoring >> Server Performance, etc. and make recommendations on upgrades because the following are not replacements for those issues. Think of this as augmenting things like additional RAM, LiteSpeed, plan upgrades. etc.
If you're uncertain on any of the following items, then please ask a manager prior to making the change.
Areas of Focus:
Caching and WordPress settings Image optimization Database optimization Apache/PHP optimization
The above areas will provide a solid basis for optimizing a WordPress installation while maintaining our standard managed services.
Caching and WordPress Settings
By default, WordPress has no built-in caching system. This can cause performance issues, specifically for large/busy sites. The solution is to install memcached system service and the associated memcache PHP module and then install/configure W3 Total Cache to utilize it.
Install memcached + PHP memcache support. Search, install, and activate the W3 Total Cache plugin via the Plugins >> Add New interface. Once the W3 Total Cache Plugin has been activated it will redirect to the Installed Plugins page and from there, click on the W3 Total Cache Settings link. Select disk-enhanced from the drop-down box for the following items:
Page cache method Select memcached from the drop-down box for the following items:
Database Cache Method Object Cache Method
Once you've selected the above options, click Save All Settings AND click on Empty Cache.
There are also other settings that are disabled by default which may help improve page load times. The following can break sites, so we do not officially recommend the following.
Performance >> Minify >> HTML & XML and click the checkbox next to: Enable, Inline CSS minification, Inline JS minification, and Line break removal. Make sure NOT to check Don't minify feeds. Click Save All Settings and if prompted, clear cache.
The Minify feature can cause javascript issues with certain plugins or load issues. We do not recommend enabling it within W3TC for these reasons!
Performance >> Browser Cache >> General and click the checkbox next to: Set expires header, Set cache control header, and Set entity tag (eTag). Click Save All Settings and if prompted, clear cache.
Why don't we use memcached for page caching?
Page Caching Benchmarks
Testing a default WordPress installation with the Theme Unit Test data imported (https://codex.wordpress.org/Theme_Unit_Test). Testing locally using the following command:
root@wiredtree [~]# ab -c 1 -n 100 http://wordtest.etcet.net/
No page caching:
Requests per second: 1.06 [#/sec] (mean)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 877 941 110.2 917 1658
Waiting: 603 651 63.8 640 1115
Total: 877 941 110.2 917 1658
Page caching w/ memcache:
Requests per second: 9.30 [#/sec] (mean)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 95 107 97.1 97 1068
Waiting: 89 100 95.5 91 1045
Total: 95 107 97.1 98 1068
Page caching w/ disk enhanced:
Requests per second: 89.15 [#/sec] (mean)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 1 11 98.9 1 990
Waiting: 1 11 96.7 1 968
Total: 1 11 98.9 1 990
As you can see, page caching using disk enhanced outperforms using memcache. To find out how it achieves this, look in the .htaccess file for the site. You'll see there are some complicated rewrite rules created by W3TC. These rewrite rules are used to serve up cache hits. With these rules in place, a cache hit will essentially be on-par with the performance of static content. This is compared to using memcache which requires PHP to be processed to perform a cache lookup.
Other WordPress caching plugins that use .htaccess rewrite rules to perform cache lookups include WP Super Cache (with mod_rewrite caching enabled).
Image Optimization
There are other useful plugins that may help reduce page load times, specifically with image-heavy sites. The plugin is called WP smush.it and will automatically optimize every image you upload by removing all of the non-essential data from the photos like creation date, camera used for the photo, gps coordinates, etc.
Search, install, and activate the WP Smush.it plugin. If prompted, click the Empty Page Cache button.
This will only smush newly uploaded images and NOT the previously uploaded images. As of version 1.4.0 there is a new, experimental Bulk Smush.it feature. You can find the link under the Media Library tab. Heed the warning that it's experimental and inform the customer about this PRIOR to running it.
Database Optimization
This section is going to deal with optimizing the database table types and making recommendations based on things like number of rows. This is not dealing with optimizing the MySQL/MariaDB configuration file itself. Having said that, the default table type for WordPress databases are MyISAM. MyISAM tables are great for simple storage are better at fulltext searches and such, but MyISAM suffers from table-level locking. This is problematic for heavily trafficked sites that rely on concurrent queries to the same table leading to the dreaded 'Locked Table' output in the process listing, queued queries, and bad performance.
The solution is to switch the tables over to InnoDB. InnoDB does row-level locking and allows multiple concurrent queries to be run against the same table. What this ultimately leads to is fewer locked table and overall faster page load times and less system resource utilization. There are caveats however:
Not all themes and/or plugins are going to be compatible with InnoDB. You need to be explicitly clear to the customer that they need to check with their developer (or theme/plugin developer) to see if they are going to work. It's not our responsibility to check for compatibility as we are just making general performance recommendations. Nearly all up-to-date core WordPress, themes, and plugins will work, but double-check with the customer prior to making any changes. The tables that you are converting must not contain FULLTEXT indexes as they are not compatible with InnoDB.
Use the following SQL query to determine if the above is true:
MySQL 5.5+
SELECT tbl.table_schema, tbl.table_name FROM
(SELECT table_schema, table_name FROM information_schema.tables WHERE 1=1 AND engine = 'MyISAM' AND table_schema NOT IN ('information_schema', 'mysql', 'performance_schema')) tbl
INNER JOIN
(SELECT table_schema, table_name FROM information_schema.statistics WHERE index_type = 'FULLTEXT') ndx
USING (table_schema, table_name);
MySQL 5.4 and Lower
SELECT tbl.table_schema, tbl.table_name FROM
(SELECT table_schema, table_name FROM information_schema.tables WHERE 1=1 AND engine = 'MyISAM' AND table_schema NOT IN ('information_schema', 'mysql')) tbl
INNER JOIN
(SELECT table_schema, table_name FROM information_schema.statistics WHERE index_type = 'FULLTEXT') ndx
USING (table_schema, table_name);
If the above doesn't return any warnings or errors, then you can safely convert the tables to InnoDB. But you ABSOLUTELY need to do the following:
Document current MySQL/PHP settings as a ticket note: cat /usr/local/cpanel/version ; echo; httpd -v; echo; /usr/local/cpanel/bin/rebuild_phpconf --current; echo; grep ^extens /usr/local/lib/php.ini; echo; php -v; echo; mysql -V; Generate backups using SQL dumps for all databases (just in case): mkdir /home/mysql.back && for i in $(mysql -BNe 'show databases'| grep -v _schema);do echo $i; sudo mysqldump $i > /home/mysql.back/$i.sql ; done
Once you have done the above, then you can proceed with the conversion:
Conversion
$ DATABASENAME="cpanelusername_dbname"
$ for t in `echo "show tables" | mysql --batch --skip-column-names $DATABASENAME`; do mysql $DATABASENAME -e "ALTER TABLE \`$t\` ENGINE = InnoDB;"; done
Another area within database optimization that can create performance issues is the row size on some of the tables. Switching over to InnoDB helps with this but if there is a table with 1 million+ rows, it's going to cause performance issues. Finding largest tables on MySQL instance is simple in MySQL 5.0+ thanks to Information Schema and you can use the following query to output that information for the top 10 tables:
SELECT CONCAT(table_schema, '.', table_name),
CONCAT(ROUND(table_rows / 1000000, 2), 'M') rows,
CONCAT(ROUND(data_length / ( 1024 * 1024 * 1024 ), 2), 'G') DATA,
CONCAT(ROUND(index_length / ( 1024 * 1024 * 1024 ), 2), 'G') idx,
CONCAT(ROUND(( data_length + index_length ) / ( 1024 * 1024 * 1024 ), 2), 'G') total_size,
ROUND(index_length / data_length, 2) idxfrac
FROM information_schema.TABLES
ORDER BY data_length + index_length DESC
LIMIT 10;
With the information from above, you can recommend that the customer take a look to see if any of the old data can be truncated (removed). The end result would be fewer rows to iterate through and faster overall performance.
Apache/PHP Optimization
While using SuPHP as the default PHP handler makes managing file and folder ownership and permissions simple, it's generally considered to be the slowest handler. And it does not allow for effective use of op-code caching systems like XCache or Zend Opcache. In order to retain the manageability of SuPHP-style permissions with the benefits of something like DSO/cgi, then we will need to compile Apache with support for mod_ruid2. Some important caveats:
LiteSpeed is not compatible with mod_ruid2. Do NOT try to install mod_ruid2 if they are running it. If they are running an older version of PHP like 5.2.x that's not in EasyApache, then you will need to work with them on upgrading that PHP version. Mod_ruid2 is still marked as experimental and while unlikely, it can cause oddities with page loads. You need to make sure that you alert the customer to this prior to recompiling Apache with mod_ruid2.
We highly recommend going with Zend Opcache over any other PHP opcode caching alternative. If the server is running PHP 5.5.x, then you just need to add zend_extension="opcache.so" to /usr/local/lib/php.ini and restart Apache for it to load.
There are two methods of installing Zend Opcache for PHP 5.3.x or 5.4.x:
Install through PECL (preferred): pecl install channel://pecl.php.net/ZendOpcache-7.0.3 Via source:
Zend Opcache Install
cd /usr/local/src
wget http://pecl.php.net/get/ZendOpcache
# to get the latest (master) build do the following instead:
tar xvfz zendopcache-7.x.x.tgz
cd zendopcache-7.x.x
phpize
whereis php-config
# set the path below
./configure --with-php-config=/usr/local/bin/php-config
make
make install
# note the opcache.so install path because you will use it below
vi /usr/local/lib/php.ini
Block Access to xmlrpc.php Server Wide
Blocking xmlrpc.php access will break Jetpack. A lot of customers and their respective clients use it. Don't blindly redirect all requests to xmlrpc.php without checking with them first (or go ahead and block it if it's causing a big problem, and then let them know what you did and how it can affect their sites!).
Step-by-step guide
Edit /usr/local/apache/conf/includes/pre_main_global.conf
Add the following to the bottom of this include file:
<IfModule mod_alias.c>
RedirectMatch 301 ^.*/xmlrpc.php$ http://127.0.0.1/
</IfModule>
Restart httpd. (service httpd restart)
Test accessing xmlrpc.php on a WordPress site on the server. (e.g. www.example.org/xmlrpc.php)
One liner to add redirect to pre_main_global and restart
echo '<IfModule mod_alias.c>
RedirectMatch 301 ^.*/xmlrpc.php$ http://127.0.0.1/
</IfModule>' >> /usr/local/apache/conf/includes/pre_main_global.conf
service httpd restart
EA4 note
/etc/apache2/conf.d/includes/pre_main_global.conf is the EasyApache 4 location. It looks like this is a hard link to/from the files in /usr/local/apache/conf/includes which is preferable as a common location between CentOS 6 and 7, and EA3 and EA4.
We can confirm that the httpd program is looking for content in /etc/apache2/conf.d/ and not /usr/local/apache/conf/:
# strace httpd -t 2>&1 | grep ^open | grep conf
Be sure to restart httpd after adding this file directive to the include file.
Comments