How to optimize your WordPress hosting – 9+ practical tips

Isn’t it true that, when you (start to) develop WordPress websites for clients, and you host them yourself, you find yourself in a situation where you need to know a lot about “stuff” other than WordPress development? In this optimizing WordPress hosting post, I provide 9+ practical tips for you, to improve WordPress hosting performance. Especially useful if you plan to develop and host WordPress websites yourself.

Learn how to optimize your WordPress hosting environment

Optimizing WordPress and WordPress hosting is a subject on which a lot is written. And therefore, this post is not about where to host your WordPress blog, or who offers the best WordPress hosting. This post is for you developers, about what you can do to tune & optimize your WordPress hosting.

Or for any other PHP web application for that matter.

This post is not about setting up high-availability, fail-over, clustering, IIS versus Nginx versus Apache, RAID 1, 5, 6, 10, different types of storage, and so on. It’s about solving performance issues. This post tackles most of your performance issues by explaining those important configuration settings for PHP & MySQL, server, security, and so on.

When reading Facebook groups like Advanced WordPress (AWP) or WordPress Development Stack Exchange, you often find questions like:

Will it affect the performance of the site if we increase the number of records in wp_posts table by 10000 records? At this moment there are 200 posts.

Of course it will affect performance, doh! :) But, you can do a lot to optimize & tune your server/VPS and WordPress hosting.

Server hardware & separate web and MySQL services

One of the most important tips I can give you: separate services.

Whether you run your WordPress blogs on a VPS or bare-metal server, make sure you separate different server types and services. A lot of problems arise from having both Apache/PHP and MySQL on the same server, because MySQL loves memory. Approximately 80% memory needs to be available to MySQL.

My advice for you is to separate them to two servers, dedicated and optimized for their purpose (web or MySQL). Sure it’ll cost you an additional $10/m for an extra DigitalOcean droplet, but trust me: it’s worth it.

Isn’t it true that, when you (start to) develop WordPress websites for clients, and you host them yourself, you find yourself in a situation where you need to know a lot about “stuff” other than WordPress development? These 10 practical tips helps you tackle the most common performance issues.

MySQL optimization

As always, to tune MySQL you need to run an as high version of MySQL as possible. That means running MySQL 5.7, MariaDB 10.1 or Percona Server 5.6, at the time of this writing. Some MySQL administration experience is recommended for optimizing these settings.

InnoDB storage engine

Make sure your MySQL database tables are of the InnoDB storage engine. Nowadays MyISAM is outdated, just like PHP 5.3/5.4, .NET 3.5 and Windows Server 2003 are. All MySQL optimizations are for the InnoDB storage engine, so switch to that.

Execute the result set of the following statement on your database to convert your old MyISAM tables to InnoDB:

SELECT CONCAT('ALTER TABLE ',table_schema,'.',table_name,' ENGINE=InnoDB;')
FROM information_schema.tables WHERE engine='MyISAM' 
AND table_schema NOT IN ('information_schema','mysql','performance_schema');

The output procuded will be something like:

ALTER TABLE db-name.table1 ENGINE=InnoDB;
ALTER TABLE db-name.table2 ENGINE=InnoDB;
ALTER TABLE db-name.table3 ENGINE=InnoDB;

InnoDB Disk I/O performance:
As of MySQL 5.5, InnoDB disk i/o performance can be increased by increasing the innodb_write_io_threads and innodb_read_io_threads settings in your MySQL server’s my.cnf file. These are very important settings. Starting with MySQL 5.5.4, you can also divide the InnoDB buffer pool into multiple instances with innodb_buffer_pool_instances.

However, one important detail to remember is:

This options takes effect only when you set the innodb_buffer_pool_size to a size of 1 gigabyte or more.

So when dividing your InnoDB buffer pool into 6 instances, you’ll need at least 6 GiB RAM…

Interesting:   Clean-up WordPress spam comments and meta data

MyISAM To InnoDB Conversion
You can easily convert MyISAM tables to InnoDB to take advantage of MySQL’s performance optimization for InnoDB. The Vevida Optimizer plugin, I co-authored, can convert these tables for you.

InnoDB innodb_log_file_size

It is important to set a proper InnoDB log file size (in Dutch, use Google Translate) in your my.cnf. You need to make the logs (there are two) big enough to hold at most an hour of so of logs. That is more than enough so that it can reorder the writes to use sequential I/O during the flushing and checkpointing process.

You can also check the recommended innodb_log_file_size with the following command:

mysql -e 'show engine innodb status\G' | awk ' BEGIN { } /Log sequence number/ {st=$4 } /Last checkpoint at/ { ed=$4; print (st-ed)/1024/1024 } '

If the Checkpoint Age is getting close to Async point, I would consider to increase innodb_log_file_size, say by 20%.

You can set the InnoDB log file size using innodb_log_file_size in your my.cnf configuration file. You need to do some calculations and monitoring before you can set its value. After setting or changing innodb_log_file_size you have to restart mysqld, and don’t forget to delete its current log files ib_logfile0 and ib_logfile1.

Setting a good innodb_log_file_size value will also resolve the MySQL error:

InnoDB: ERROR: the age of the last checkpoint is 'n', which exceeds the log group capacity 'o'.

Pro Tip: update 1 Jun 2016: Percona’s Vadim Tkachenko posted an interesting article called “What is a big innodb_log_file_size?“. The article dives pretty deep into the inner workings of InnoDB and its logs.

MySQL query cache

You want to have MySQL query cache enabled on your MySQL database server, and have it configured with a proper value. Enable MySQL query cache in your my.cnf by setting:

query_cache_type = 1

And you can configure its size with query_cache_size = ...M

Check MySQL’s Qcache_lowmem_prunes regularly, and when it increases a lot you need to increase the query_cache_size value. You can check Qcache_lowmem_prunes and other query cache values on the MySQL command line interface. The following SHOW-command displays most relevant Qcache values:

mysql> SHOW STATUS LIKE 'q%';

Let’s do some math:

  • query cache hit ratio: Qcache_hits / (Qcache_hits + Com_select);
  • query_cache_size: first determine the average query size: (query_cache_size – Qcache_free_memory) / Qcache_queries_in_cache;
  • query Cache fragmentation: Qcache_free_blocks =~ Qcache_total_blocks / 2;

When qcache_free_blocks is about equal to qcache_total_blocks / 2, there is query-cache fragmentation and you need to flush MySQL query cache.

RAM, SSD, and CPU

MySQL loves memory (RAM). Make sure your server has plenty of it, and some more. About 80% of the RAM needs to be available for MySQL, and when you start dividing Innodb_buffer_pool_instances, query-cache, and various buffer sizes, RAM consumption increases rapidly.

Because SSD offers the best read/write (disk I/O) performance, SSD’s are a must too.

If you don’t need MySQL’s binlog -or binary log, then disable it. Having MySQL’s binary log enabled will increase read/write operations on your disk. Do enable the slow query log to identify slow running database queries. The following example enables the slow query log for queries running three (3) seconds or more:

slow_query_log = 1
long_query_time = 3.000000
slow_query_log_file = /var/log/mysql/slow.log

Through this MySQL slow log, I once found that the autoload column of the wp_options table wasn’t indexed. You can fix that easily too: wp_options table autoload index.

Don’t save on CPU power, because MySQL likes to use the CPU for heavy calculations as well.

Check MySQL’s Qcache_lowmem_prunes regularly, when it increases a lot you need to increase the query_cache_size value. For best I/O performance, SSD’s are a must too.

Web server optimization – PHP

(No Apache, Nginx or IIS optimizations here, as I feel the web server is not often the bottleneck if WordPress sites are slow – but maybe you have a great tip for this? Share a comment!).

Interesting:   How to clean up Contact Form 7 temporary captcha files on IIS web servers

PHP Optimization:
As with MySQL versions, go with the highest possible version: 5.6.18, or preferably 7.0.3 -at the time of this writing, if all your code is PHP 7 compatible. Some PHP settings need optimization too, I discuss them below:

PHP OPcache

Zend OPcache PHP extension is in PHP nowadays, so why not enable OPcache and use it? I recommend you do. In your php.ini verify the following line is not commented out:

zend_extension = php_opcache.dll

You have to properly optimize OPcache to take fully advantage of this opcode cache.

For IIS, set up WinCache and be sure to disable WinCache opcode cache in favor of OPcache’s opcode cache:

extension=php_wincache.dll
wincache.ocenabled = 0
wincache.ocachesize = 0

Note: The .dll-part comes from my Windows Server hosting environment.

Be careful when enabling WinCache server-wide though, it might consume all available memory.

PHP realpath_cache_size

PHP realpath_cache_size sets the size of the realpath cache to be used by PHP (e.g, the file locations on disk). Increasing realpath_cache_size can improve PHP performance a lot, and this value should be increased on systems where PHP opens many files.

Your WordPress Hosting at Warp Speed - img. credits: Christian Daryanto Limas @ flickr

Your WordPress Hosting at Warp Speed – img. credits: Christian Daryanto Limas @ flickr

WordPress optimization

Here are some quick tips to optimize WordPress environment and sites.

WordPress development tips

An area I’m not very experienced in, but here goes… A tip: always (try to) follow WordPress development guidelines. For optimized PHP code, check out the PHP Benchmark tests, it’s a real must!

Also, it’s recommended to try to avoid catching exceptions in PHP. Exception handling can cause a performance penalty: Checking the performance of PHP exceptions, Speed performance of Trying and catching Exceptions in PHP.

Optimizing your WordPress hosting environment, the conclusion

When you develop WordPress websites for clients, and you want to host them yourself, you find yourself in a situation where you need to know a lot about systems administration “stuff“. Completely different topics than WordPress development. Like MySQL configuration, web server configuration, PHP settings, server security, and so on.

In this optimize WordPress hosting post, I provided you 10 important and practical tips to improve your WordPress hosting performance. This post tackled most of your performance issues by explaining those important configuration settings for PHP & MySQL, server & security. Especially useful when you plan to host WordPress websites yourself.

As you might have noticed, MySQL optimization is very important for your hosting environment… Do you want to learn more about InnoDB performance improvements in MySQL? Then have a look at my tuning InnoDB buffer pool instances post. The InnoDB Storage Engine chapter on mysql.com contains a lot of valuable information too.

Please follow and like us:
12 replies
  1. Jamil Ahmed
    Jamil Ahmed says:

    Hello Jan, your post is interesting. I have to say here that you have done a really good job by publishing the article above. This is really going to help a huge audience searching for such information. Have to seen Cloudways? Its a super fast managed cloud hosting platform. You can explore all its features by getting started for free. managed WordPress hosting




    0



    0
    Reply
      • Jamil Ahmed
        Jamil Ahmed says:

        Cloudways isn’t a CDN, they offer optimized WordPress hosting servers of four popular clouds (DigitalOcean, Vultr, GCE, AWS).
        The stack they have is Nginx, Varnish, Apache, Memcached, MySQL, PHP 5.6.




        0



        0
        Reply
  2. Milos
    Milos says:

    Really good article, it tackles some important topics. I feel like the “avoid catching exceptions” part is unnecessary, It’s the kind of micro-optimization I would never recommend.




    0



    0
    Reply
    • Jan Reilink
      Jan Reilink says:

      Hi Milos, thank you for your comment!

      I’ve had my thoughts and questions about including the “catching exceptions” part. And true, it’s not really something for WordPress, but more for general PHP. I feel it can make a difference, especially when done wrong (I’ve tested the examples a few years ago and could reproduce the results, I don’t know if the exception handling has improved in PHP since).




      0



      0
      Reply
  3. Chris Dempsey
    Chris Dempsey says:

    Where you discuss “install a cache plugin like WP-Super-Cache” you could add a link to your article with the rewrite rules for WP Super Cache – https://www.saotn.org/9-wordpress-plugins-you-need/#WP-Super-Cache_Rewrite_fix

    Rewriting to the static files offers a huge performance gain for five minutes work. And you don’t need to dig into the hardware setup or software config.

    I wish more of my resellers would do this instead of hammering MySQL over and over.




    0



    0
    Reply
    • Jan Reilink
      Jan Reilink says:

      Hi Chris, thank you for your comment and suggestion. I kind of forgot about that link :-) I just spotted an error, there can’t be two or more HTTP_USER_AGENT lines when logicalGrouping is set to “MatchAll”. I’ve fixed that now.

      All you can do is pointing your resellers to information like this and urge them to setting it up.

      There are more WP-Super-Cache enhancements you can make: how about minifying the static supercache files or serve gzip supercache files on IIS (rewrites included). I also did an enhancement request for web.config support in WP-Super-Cache: issue #93 on GitHub.




      0



      0
      Reply
  4. leo
    leo says:

    Thanks for the great article.

    i have a few questions regarding opcache on php 5.x up to 7.x, i read that php-fpm doesn’t play well with opcache in shared hosting environments, allowing one site to read another site’s files. This is because opcache=on means all sites share the same memory pool, and php-fpm doesn’t check for ACLs. My question is whether the same security issue occur on IIS shared hosting environment where is opcache is a shared memory pool? Does opcache run in the context of a site’s ApplicationPoolIdentity which check for ACLs before serving from the shared opcache memory pool?

    I know a way of testing by using url_fopen on php to test if a crafty file on one site can access files of another site because of opcache enabled. Solution would be to turn off url_fopen like in most production envs but that still doesn’t eliminate all possibilities at least in a shared hosting for environment.




    0



    0
    Reply
    • Jan Reilink
      Jan Reilink says:

      Hi Leo, thank you for your interesting question!

      Yes, under rare circumstances it was possible to get control of OPcache memory files, under PHP 7 though. You may want to read Binary webshell through OPcache in PHP 7 for more information on this. This only was an issue when file based caching is enabled with opcache.file_cache=. On the Windows IIS web servers I manage, it is kept in memory.

      Furthermore, it is recommended to ensure file permissions are properly in place, so website/user_A cannot access temporary files created by website/user_B. Even when they share the temporary folder. Where possible you must provide a dedicated temporary location on a per website/user basis.




      0



      0
      Reply

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published.