WordPress Optimization
Contents
- 1 How to Optimize Wordpress on a cPanel Server
- 2 WordPress Performance Tests
- 3 Installation Steps
- 3.1 MySQL 5.6 Configuration for Wordpress
- 3.2 Apache 2.4 Event Configuration for Wordpress
- 3.3 Varnish Installation and Configuration for Wordpress
- 3.4 PHP 5.5 Configuration for Wordpress
- 3.5 PHP-FPM Configuration
- 3.6 Memcached Installation and Configuration for Wordpress
- 3.7 WordPress Configuration
- 3.8 cPanel Configuration
- 3.9 Optimized .htaccess for WordPress
How to Optimize Wordpress on a cPanel Server
This page is meant to help improve Wordpress performance on a cPanel server. There is quite a lot of customization that needs to be done, but if you follow this guide you can haz fast Wordpress!
WordPress Performance Tests
By using the guide below, you can significantly improve Wordpress performance. For my tests I used WordPress 4.0.1 and did not create any posts, or add any content to the blog. I did this to set a baseline for performance, once you start adding in content it's hard to really compare results.
I tested out performance on [LiquidWeb's 1GB SSD VPS] The VPS has 2 vCPUs and 1GB of RAM along with 50GB all SSD storage.
For the "Default" run I used a newly created cPanel server, installed WordPress and started to running tests. I used MySQL 5.6, Apache 2.4 Prefork with PHP 5.4 using SuPHP as the handler. I did not have an optimized .htaccess file in place, Varnish was not installed, memcached was not installed, and w3totalcache was not used for this run. Obviously this is not how most people run WordPress, but I wanted to use the most basic configuration and no optimizations to show the "worst case" WordPress performance.
I used Apache Benchmark to test out the site. I ran this from a remote location, so this was NOT run on the same server that hosted the Wordpress site. This command will use 5 concurrent connections and will load the site 1000 times.
ab -c 5 -n 1000 http://mytestdomain.lol/
As you can see, with the default configuration I get around 13 Requests per second. The response time for 95% of requests was 456ms. This isn't amazing, but not terrible. The server was able to handle the load without issue, but view the next set of results to see what happens once we optimize the server a bit.
Server Software: Apache/2.4.10 Server Hostname: mytestdomain.lol Server Port: 80 Document Path: / Document Length: 7418 bytes Concurrency Level: 5 Time taken for tests: 76.810 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 7675000 bytes HTML transferred: 7418000 bytes Requests per second: 13.02 [#/sec] (mean) Time per request: 384.050 [ms] (mean) Time per request: 76.810 [ms] (mean, across all concurrent requests) Transfer rate: 97.58 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 1 2 2.1 1 20 Processing: 274 382 51.1 382 655 Waiting: 219 309 44.2 310 491 Total: 275 384 51.2 385 655 Percentage of the requests served within a certain time (ms) 50% 385 66% 411 75% 425 80% 433 90% 447 95% 456 98% 468 99% 478 100% 655 (longest request)
Once we installed PHP-FPM, Varnish, Memcached, Apache Event, applied .htaccess, and installed w3totalcache we get much better results. With the configuration listed below my test site was able to process 805 Requests per second. The response time for 95% of requests was 11ms which is insane. As you can see if you modify the default cPanel settings you can get significant performance gains for a basic Wordpress site.
Server Software: Apache/2.4.10 Server Hostname: mytestdomain.lol Server Port: 80 Document Path: / Document Length: 7753 bytes Concurrency Level: 5 Time taken for tests: 1.242 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 8124694 bytes HTML transferred: 7753000 bytes Requests per second: 805.07 [#/sec] (mean) Time per request: 6.211 [ms] (mean) Time per request: 1.242 [ms] (mean, across all concurrent requests) Transfer rate: 6387.65 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 2 3 1.1 3 11 Processing: 1 3 1.1 3 12 Waiting: 1 3 1.1 3 12 Total: 4 6 2.0 6 17 Percentage of the requests served within a certain time (ms) 50% 6 66% 6 75% 7 80% 7 90% 9 95% 10 98% 13 99% 14 100% 17 (longest request)
Installation Steps
MySQL 5.6 Configuration for Wordpress
Ensure that MySQL 5.6 is installed and that innodb_buffer_pool_size is set. Please note that you must move the old innodb logs out of the way, stop MySQL and then start it up again. If you do not do this then MySQL will fail to start and you will have downtime on your hands until you remove the old log files.
This configuration should work well for most servers with 1GB or 2GB of RAM.
cat /etc/my.cnf [mysqld] max_allowed_packet=268435456 open_files_limit=10000 innodb_buffer_pool_size = 64M innodb_log_file_size = 128M key_buffer_size = 64M innodb_file_per_table
Apache 2.4 Event Configuration for Wordpress
cPanel Tweak Settings Listen Port
In WHM Tweak Settings. Change the default Apache listen IP and port to 127.0.0.1:8080 and apply the changes before any accounts are created. Leave the SSL IP and port alone, apache will still be handling ssl traffic.
cPanel Apache Event Configuration
Because cPanel likes to override the main httpd.conf file, which is modified also by WHM global configuration, we will want to put an event ifmodule block in pre_virtualhost_global.conf, this will override any dumb settings cpanel tries to apply, keeping our event settings at the defaults, which should be fine for most websites.
vim /usr/local/apache/conf/includes/pre_virtualhost_global.conf ### KeepAlive On KeepAliveTimeout 5 MaxKeepAliveRequests 100 <IfModule event.c> ThreadsPerChild 64 ServerLimit 16 MaxRequestWorkers 1024 StartServers 3 MaxConnectionsPerChild 0 ### </IfModule>
Apache Event MPM and Version Info
Using Apache 2.4 EVENT, we are NOT using mod_php or SuPHP as the handler, we are using FastCGI to pass off PHP handling to PHP-FPM.
httpd -V Server version: Apache/2.4.10 (Unix) Server built: Nov 26 2014 14:49:27 Cpanel::Easy::Apache v3.26.10 rev9999 Server's Module Magic Number: 20120211:36 Server loaded: APR 1.5.1, APR-UTIL 1.5.4 Compiled using: APR 1.5.1, APR-UTIL 1.5.4 Architecture: 64-bit Server MPM: event threaded: yes (fixed thread count) forked: yes (variable process count) Server compiled with.... -D APR_HAS_SENDFILE -D APR_HAS_MMAP -D APR_HAVE_IPV6 (IPv4-mapped addresses disabled) -D APR_USE_SYSVSEM_SERIALIZE -D APR_USE_PTHREAD_SERIALIZE -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT -D APR_HAS_OTHER_CHILD -D AP_HAVE_RELIABLE_PIPED_LOGS -D DYNAMIC_MODULE_LIMIT=256 -D HTTPD_ROOT="/usr/local/apache" -D SUEXEC_BIN="/usr/local/apache/bin/suexec" -D DEFAULT_PIDLOG="logs/httpd.pid" -D DEFAULT_SCOREBOARD="logs/apache_runtime_status" -D DEFAULT_ERRORLOG="logs/error_log" -D AP_TYPES_CONFIG_FILE="conf/mime.types" -D SERVER_CONFIG_FILE="conf/httpd.conf"
Varnish Installation and Configuration for Wordpress
Installation Method
This seems to be the simplest way to install Varnish.
rpm --nosignature -i https://repo.varnish-cache.org/redhat/varnish-4.0.el6.rpm wget https://dl.fedoraproject.org/pub/epel/6/x86_64/jemalloc-3.6.0-1.el6.x86_64.rpm rpm -i jemalloc-3.6.0-1.el6.x86_64.rpm yum install varnish chkconfig varnish on
/etc/sysconfig/varnish
This file is used to configure the varnish daemon. We will be changing the listening port to 80 and configuring the malloc storage size based on the size of the instance used. I set this to 128MB for the 1GB VPS, you may need to tune this if you run into memory issues.
For this file, we really only need to worry about two items, the listening port and the storage size. The listen port should always be set to 80. The storage size should scale with the instance size (how much RAM your server has). I was able to hammer my server with 1000 requests @ 10 concurrent connections and my server didn't even break a sweat with only 1GB, so this should be pretty good for most sites.
vim /etc/sysconfig/varnish VARNISH_LISTEN_PORT=80 VARNISH_STORAGE_SIZE=128M
/etc/varnish/default.vcl
This file defines what the backend servers are (in this case that is apache 127.0.0.1:8080). It also defines how varnish caches things, and handles them. We are basically just going to define the ACL for Purging so that W3Total cache can purge if needed.
Really, for the most part we should just be able to set it and forget it, but if you run into issues you will modify the VCL. By default Varnish is able to cache a lot with no special VCL in place. If you run into issues with SSL, or Sessions or whatever you might want to add in some VCL to handle this, otherwise the file below should be fine.
vim /etc/varnish/default.vcl backend default { .host = "127.0.0.1"; .port = "8080"; } acl purge { "localhost"; } sub vcl_recv { if (req.method == "PURGE") { if (!client.ip ~ purge) { return(synth(405,"Not allowed.")); } return (purge); } }
PHP 5.5 Configuration for Wordpress
Other than making sure EA is using PHP 5.5.x as the main version there is nothing to configure here really (in terms of modules or whatnot).
Zend OPcache Configuration
Assuming you are using PHP 5.5+ you should be able to easily enable PHP's built in OPcache by modifying /usr/local/lib/php.ini. Make sure that opcache.so is located in your extensions directory that is specified in the php.ini file. Once you have edited the file restart php-fpm.
extension=opcache.so opcache.memory_consumption=64 opcache.interned_strings_buffer=4 opcache.max_accelerated_files=7963 opcache.revalidate_freq=120 opcache.fast_shutdown=1 opcache.enable_cli=1 opcache.enable=1
PHP Version Info
As long as we are using PHP 5.5 and up, you should be good.
php -v PHP 5.5.19 (cli) (built: Nov 26 2014 14:53:33) Copyright (c) 1997-2014 The PHP Group Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies with the ionCube PHP Loader v4.6.1, Copyright (c) 2002-2014, by ionCube Ltd. with Suhosin v0.9.36, Copyright (c) 2007-2014, by SektionEins GmbH
cPanel PHP Handler Info
Make sure you set the default PHP handler to FCGI, if you don't do this then the PHP-FPM script will not complete and you might have down websites to deal with.
/usr/local/cpanel/bin/rebuild_phpconf --current Available handlers: fcgi cgi none DEFAULT PHP: 5 PHP4 SAPI: none PHP5 SAPI: fcgi SUEXEC: enabled RUID2: not installed
PHP-FPM Configuration
Installation method for PHP-FPM
This script seems to work well for installing and configuring PHP-FPM on a cPanel account. The script will install PHP-FPM, generate per domain configuration files and modify httpd.conf to configure existing domains to use PHP-FPM. You will need to make sure you enable FCGI via Easy Apache and select the default PHP handler to be FCGI.
wget http://sysally.net/cphstack.sh chmod a+x cphstack.sh ./cphstack.sh install service php-fpm start
PHP-FPM Config File: /usr/local/etc/php-fpm.conf
This is the global configuration file for PHP-FPM. There is not much to mess with here, at the end of the file there is an includes directory listed which houses the individual vhost configs. NOTE The memcache.so line was added by me to get the memcache module to work. Everything else is at the default, but keep in mind that the .conf files under the include directory will take precedence over this file.
[global] pid = /var/run/php-fpm/php-fpm.pid error_log = /var/log/php-fpm.log [nobody] user = nobody group = nobody listen = 127.0.0.1:9000 pm = ondemand pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3 php_admin_value[extension] = memcache.so include=/opt/xstack/cphstack/php-fpm.pool.d/*.conf
PHP-FPM Config File(s): /opt/xstack/cphstack/php-fpm.pool.d/*.conf
The per domain config files go in this directory. Each file is named after the domain, which is nice, for example, on my test server the only file under this directory was:
-rw-r--r-- 1 root root 18163 Nov 26 14:56 domain.conf
My config file has the following settings:
;;;;;;;;;;;;;;;;;;;; ; Pool Definition ; ;;;;;;;;;;;;;;;;;;;; [user] user = user group = user listen = /var/run/php-fpm/user.sock listen.mode = 0666 pm = ondemand pm.max_children = 15 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3 pm.max_requests = 256
Memcached Installation and Configuration for Wordpress
yum install memcached vim /etc/memcached.conf ##Modify the file to include these lines. You can raise -m from 64 to however much memory you want memcached to use, this configuration file is meant for a smaller server with 1GB or 2GB of RAM. ## -m 64 -p 11211 -u nobody -l 127.0.0.1
Once you have finalized the memcached configuration file, go ahead and start up memcached and use chkconfig to make sure that memcached starts up on reboot.
service memcached start chkconfig memcached on
Now you need to install the memcache PHP module so that WordPress can use it with w3totalcache
pecl install memcache echo "extension=memcache.so" >> /usr/local/lib/php.ini service httpd restart
Run the command below to make sure that the memcache PHP module is loaded.
php -m | grep memcache
WordPress Configuration
Nothing too special here, installed the latest version of Wordpress and installed w3totalcache/
W3TotalCache
This is how I enabled the plugin. No need to really worry about page cache since we are caching the hell out of everything already.
Page Cache: Disabled Minify: Disabled Database Cache: Enabled, uses Memcached Object Cache: Enabled, uses Memcached Browser Cache: Enabled CDN: Disabled Reverse Proxy: Enable Varnish Cache, server 127.0.0.1
cPanel Configuration
Tweak Settings
Updating Use pigz from Off to On. Updating Bandwidth limit check from On to Off Updating Allow cPanel & WHM to determine the best value for your MySQL max_allowed_packet configuration? from On to Off Updating Allow cPanel & WHM to determine the best value for your MySQL open_files_limit configuration? from On to Off. Updating Critical load threshold from “10” to # of CPUs (autodetect). Apache non-SSL IP/port 127.0.0.1:8080
Optimized .htaccess for WordPress
<ifmodule mod_deflate.c> AddOutputFilterByType DEFLATE text/text text/plain text/xml text/css application/x-javascript application/javascript text/javascript </ifmodule> <IfModule mod_expires.c> ExpiresActive On ExpiresDefault "access plus 5 seconds" ExpiresByType text/css A31536000 ExpiresByType application/x-javascript A31536000 # Text ExpiresByType text/css A31536000 ExpiresByType application/x-javascript A31536000 ExpiresByType text/html A3600 ExpiresByType text/richtext A3600 ExpiresByType text/plain A3600 ExpiresByType text/xml A3600 # Image ExpiresByType image/gif A31536000 ExpiresByType image/x-icon A31536000 ExpiresByType image/jpeg A31536000 ExpiresByType image/png A31536000 ExpiresByType image/svg+xml A31536000 # Video ExpiresByType video/asf A31536000 ExpiresByType video/avi A31536000 ExpiresByType video/quicktime A31536000 ExpiresByType video/mp4 A31536000 ExpiresByType video/mpeg A31536000 # PDF ExpiresByType application/pdf A31536000 # Flash ExpiresByType application/x-shockwave-flash A31536000 # Font ExpiresByType application/x-font-ttf A31536000 ExpiresByType application/vnd.ms-fontobject A31536000 ExpiresByType application/x-font-otf A31536000 # Audio ExpiresByType audio/mpeg A31536000 ExpiresByType audio/ogg A31536000 ExpiresByType audio/wav A31536000 ExpiresByType audio/wma A31536000 # Zip/Tar ExpiresByType application/x-tar A31536000 ExpiresByType application/x-gzip A31536000 ExpiresByType application/zip A31536000 </IfModule> Options -Indexes