In previous post “How to Speed Up Your WordPress – The Definitive Guide“, we discussed about some principles that need to consider when improving the performance of your WordPress site, like choosing “right” web host, reduce HTTP requests, doing cache, optimizing static files, etc.

In this post, I documented all the steps I have done to bring a fast WordPress blog.

At The Beginning

Before we started, there are some points that I want to make.

  • First of all, the goal of optimizing WordPress must not be simply pursuing a “high” ranking / score on those online testing tools. Instead, providing a better browsing experience to visitors should be the one we need to focus on. In other words, we need to find a balance between “reducing page load time” and “providing vivid presentation by including more interaction elements as well as media contents” – compromise is acceptable!
  • The practice I did may not be suitable for everyone.
  • For the infrastructure, I’m choosing the most open one – “cloud server” which can provide me the most flexibility in tuning the system. One of the reasons is to do learning and practicing. If you are choosing some sophistic platforms (like WP Engine, Synthesis,, etc.), you may be still able to get all the outcome (probably even better) without taking this so much pain. :)
  • For WordPress theme and plugins, I’m only picking up the minimal set that can serve my purpose of WordPress. At some point, I even abandoned certain plugin (like Crayon Syntax Highlighter) for better performance.
  • About the plugins I choose and the way of how I use them, it may not be the best choice. For example, the famous WordPress chat plugin – W3 Total Cache, since I just use the free version, it may have some “pro” features that I did not use.
  • All in all, the purpose of this post is just to share my adventure to someone who is also like to build their own WordPress site from the scratch.

OK, if you are ready, let’s begin to customize the WordPress!

Tweak WordPress

Image source:

Web Host

For “web host”, I choose to use a cloud server on AWS – an EC2 instance for the most flexibility. The server is running Ubuntu 12.04 LTS.

About the “Web Server Software”, i’m using Nginx without any doubt. So the full stack is LEMP (Linux, Nginx, MySQL, PHP).

Reference reading: Apache vs Nginx: Practical Considerations

Following the following two posts to get your server and WordPress ready.

If you are running Ubuntu 14.04, you can also check these two posts:

PHP Extensions

Here are a list of PHP extensions your need to install in order to support some optimization operations, like caching, compression, CDN, etc.


sudo apt-get install php5-curl


Should be included in Ubuntu already.

Opcode cache

In Ubuntu 12.04, you need to install Alternative PHP Cache (APC).

sudo apt-get install php-apc

Ubuntu 14.04 ships with PHP 5.5 which includes its built-in Opcache cache. You just need to enable it in php.ini file.

# change into

Reference reading: PHP INstallation


Follow this post to install MemcacheHow To Install and Use Memcache on Ubuntu 12.04.

For Ubuntu 14.04, How To Install and Use Memcache on Ubuntu 14.04


udo apt-get install php5-tidy

Reference reading: PHP: Tidy Manual

Server Configurations

Still have several server configurations need to do.

Modify “Maximum upload file size”

By default, WordPress comes with “2 MB” in “Maximum upload file size” which is relatively small, especially if you need to upload “Retina” images.

First, change the following settings in Nginx configuration file /etc/nginx/nginx.conf. It can be under http or location block.

http {
client_max_body_size 100m;

Second, change settings in PHP ini file. Assumed you have installed FPM(FastCGI Process Manager), you will need to modify “twoini files – /etc/php5/fpm/php.ini and /etc/php5/cli/php.ini.

upload_max_filesize = 100M
post_max_size = 100M

Last, you will need to modify the “WordPress Memory Limit” which is done in the file wp-config.php.

define('WP_MEMORY_LIMIT', '100M');

Once done, restart both Nginx and php-fpm.

service nginx restart
service php-fpm restart

Be sure the “limit” needs to be consistent throughout the three places.

Increase “timeout” between PHP-FPM and Nginx

Adding fastcgi_read_timeout directive to the Nginx config file of you WordPress sites, in my case it is /etc/nginx/sites-available/, under the location block which is used to process all php files.

location ~ \.php$ {
# set it to 10 min here.
fastcgi_read_timeout 600;

You can also choose to add it to http or server block. Here it mainly solves the issue when WordPress takes too long to process some heavy duty (like optimizing image, generating thumbnails, etc.) to cause the connection “timeout”. If you look into Nginx’s error_log, you will see an error similar to this:

2015/08/02 16:46:33 [error] 31010#0: *7408 upstream timed out (110: Connection timed out) while reading response header from upstream,
client:, server:, request: "GET /how-to-speed-up-your-wordpress-the-definitive-guide/
HTTP/1.1", upstream: "fastcgi://unix:/var/run/php5-fpm.sock", host: ""

WordPress Setup

Here is the list of plugins I picked for running my normal WordPress.


Akismet is the Anti-spam engine shipped with WordPress 4.2.4. It can be used for free and basically it does the job quite well. So I keep it.

Jetpack by

Jetpack by is the new “must-have” plugin that you should add. Here are the modules I activated:

  • Markdown – must have! I usually do post by Markdown.
  • Stats – A very decent “stats” tool. One of the good benefits is you can access to it via “WordPress” app on your mobile device.
  • Shortcode Embeds
  • Shortlinks – Can help you generate short link (like for your post.
  • Site Verification
  • Sharing – Another “must-have” one. By using it, you can add “sharing” button to your page.
  • Publicize – Help you automatically share new post to your social network.

If you are not going to use CDN, you can also enable “Photon” which can help you host your images on the CDN of

“Jetpack” also provides so many other good modules. Be sure to take a close look.

Google Analytics Dashboard for WP

Google Analytics Dashboard for WP is a very standard “Google Analytics” plugin which can help you link your site with Google Analytics,

This plugin also has a decent “front-end” UI. I did not enable it otherwise it will break the settings of “deferring javascript and CSS”.

WP External Links

If your post usually contains many external links (like me :)), WP External Links can definitely help you. It comes with a lot of detailed configurations.

Do Caching

Usually the best way of doing caching is to pick up a WP plugin and W3 Total Cache is always the first choice.

Here is my general settings:

  • Page Cache – Disabled. Instead, I’m using fastcgi_cache provided by Nginx natively.
  • Minify – Disabled. It is quite difficult to configure in right. And also, it seems that the “minified” file cannot be “compressed” since it is not shipped as “static file”.
  • Database Cache – Enabled and use “Memcache”.
  • Object Cache – Enabled and use “Memcache”.
  • Browser Cache – Disabled. The settings of “Cache-Control / expires” never work to me. So I decided to do it manually on Nginx config.
  • CDN – Enabled and I’m using Amazon Cloudfront. I also choose the “CDN Type” as “Generic Mirror” just simply because the native “Amazon CloudFront” option does not work to me.
  • Besides “Amazon Cloudfront”, MaxCDN is also recommended.
  • Reverse Proxy – Disabled. It is mainly for high-end WordPress site which also needs extra server to configure.

You can check the following posts to learn how to configure W3 Total Cache. If “Page Cache” “Browser Cache”, or even “Minify” work for you, you will save a lot of time. :)

Page Cache

Nginx has built-in support for fastcgi_cache but it doesn’t have mechanism to purge cached content built-in. So, I installed a 3rd-party module ngx_cache_purge to help control the cache purge.

Check out this port (fastcgi_cache with conditional purging) to learn how to install ngx_cache_purge module and configure Nginx. A new WordPress plugin “Nginx Helper” is also installed to help Nginx find out which page to purge and when to purge. This plugin provides you a good UI to do configuration as well as perform manual purge.

Test Your Setup

To test if fastcgi_cache is working, we add upstream_cache_status variable to test cache. Add the following line to /etc/nginx/nginx.conf file, under http block:

add_header rt-Fastcgi-Cache $upstream_cache_status;

After restart Nginx, check HTTP response header for any page and you should see something like this:

HTTP/1.1 200 OK
Server: nginx/1.8.0
Date: Thu, 06 Aug 2015 23:58:43 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/5.3.10-1ubuntu3.19
Link: <>; rel=shortlink
# Is this line shown, fastcgi_cache is working~~
rt-Fastcgi-Cache: HIT
ontent-Encoding: gzip

Browser Cache

Instead of using the “Browser Cache” part of W3 Total Cache, I decide to do it directly on Nginx config file.

Add the something like the following to your site’s config file, like /etc/nginx/sites-available/

# for css and js files
location ~* \.(css|htc|less|js|js2|js3|js4)$ {
access_log off;
log_not_found off;
expires 1M;
add_header Cache-Control "public";
# for all other static files
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off;
log_not_found off;
expires 1M;

Change the value of expires to the time that how long you want to cache these files, like 1M – 1 month, 1d – 1 day, max, etc. )

The max parameter sets expires to the value Thu, 31 Dec 2037 23:55:55 GMT.

Test It

Run the following command in terminal to load one of your WordPress image.

curl -I

And you will see some output like this:

HTTP/1.1 200 OK
Content-Type: image/png
Content-Length: 362545
Connection: keep-alive
Server: nginx/1.8.0
Date: Thu, 06 Aug 2015 23:49:52 GMT
Last-Modified: Thu, 06 Aug 2015 01:16:13 GMT
ETag: "55c2b55d-58831"
Expires: Thu, 31 Dec 2037 23:55:55 GMT
Cache-Control: max-age=315360000
Accept-Ranges: bytes
X-Cache: Miss from cloudfront
Via: 1.1 (CloudFront)
X-Amz-Cf-Id: n5hc9O8rwkM4OApnT4dZm_Vg-hW2epehwxjfNzBISMfQahS2rfROMA==

If you can see the line “Expires” and “Cache-Control”, then the browser cache is working~~.


As mentioned above, I’m using W3 Total Cache to help setup the Amazon Cloudfront. Check this post (Amazon Cloudfront CDN with W3 Total Cache WordPress) for detailed instruction. This post also explain why “Generic Mirror” should be a good option.

Amazon Cloudfront

Webfonts with CDN

If you are using Amazon Cloudfront like me, you may has the issue in delivering “Webfonts” (file extension in woff) from the CDN when you are using FireFox and IE.

This issue is mainly cause by the “high” restriction of same-origin policy on Firefox and IE – since you media file on Cloudfront are on a separate domain than your WordPress site.

Since I’m using Generic Mirror on CDN which sets the site as the origin. So, I need to set Access-Control-Allow-Origin on my side. Add the following lines to /etc/nginx/nginx/sites-available/

location ~ \.(ttf|ttc|otf|eot|woff|font.css)$ {
add_header Access-Control-Allow-Origin "";
expires 1M; add_header Cache-Control "public";

You can also set wildcard * to Access-Control-Allow-Origin to allow every domain.

If you are using S3 as the origin of Cloudfront, you need to configure the CORS headers.

See this post (Hosting Web fonts on a CDN? You’re going to need some CORS.) for more details.

If you are using MaxCDN, check this post (How to use CDN with Webfonts) to get it setup.

Reverse Proxy

If you are interested in setting it up, check this post (Optimizing WordPress with Varnish and W3 Total Cache). It shows how to make Varnish Cache works with W3 Total Cache.

Optimize Static Content

This part mainly includes several steps:

  • Enable gzip compression
  • Minify HTML, JS, CSS files
  • Defer JS, CSS files
  • Optimize images

Enable gzip Compression

Use online testing tools (like GTmetrix) to check if your site has gzip compression enabled properly.

If not, make sure /etc/nginx/nginx.conf file contains the following lines without commented out:

gzip on;
gzip_disable "msie6";

gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/css text/x-component application/x-javascript application/javascript text/javascript text/x-js text/richtext image/svg+xml text/plain text/xsd text/xsl text/xml image/x-icon;

The line gzip on turns on “gzip compression” throughout the server. You can also add gzip off under a server or location block to turn off gzip compression for one or more sites/regions..

The value of gzip_types is a list of MIME-types that you want to set it on on “gzip compression”.

From the gzip_types list, text/html is implied and cannot be turned off. text/css and application/x-javascript are for CSS and JS files.

Check the HTTP header of your CSS or JS file, and you will see the line Content-Encoding: gzip is included.

HTTP/1.1 200 OK
Content-Type: application/javascript
Transfer-Encoding: chunked
Connection: keep-alive
Server: nginx/1.8.0
Date: Thu, 06 Aug 2015 23:58:43 GMT
Last-Modified: Thu, 06 Aug 2015 04:14:39 GMT
ETag: W/"55c2df2f-2867f"
Expires: Sat, 05 Sep 2015 23:58:43 GMT
Cache-Control: max-age=2592000
Cache-Control: public
Content-Encoding: gzip
Vary: Accept-Encoding
X-Cache: Miss from cloudfront
Via: 1.1 (CloudFront)
X-Amz-Cf-Id: DU4sOkejkw9C5AIGxv34PmY9aSwCNiMl4w_YmgaD6Vyti5cZ5YlETQ==

Minify HTML, JS, CSS files & Defer JS, CSS files

Instead of using W3 Total Cache, I’m using another plugin Autoptimize which is a small powerful plugin for the compression, summarization, and minimization of CSS and JavaScript files. It also provides a feature to help do “CSS defer” (“Inline and Defer CSS?” in Settings).

For JS files, once enabled, Autoptimize can do magnification and “defer” automatically.

For CSS files, you need to enabled “Inline and Defer CSS?” to let Autoptimize defers the CSS files. However, you cannot simply put all CSS files at the bottom of the page, after the HTML page being rendered. The “Above-the-fold” contents still need to be rendered “with” CSS. So we need to include a minimized set of CSS into the HTML page, as “inline CSS”.

In order to find this minimized set, we can use this tool (Critical Path CSS Generator) to help decide the CSS needed for displaying the “above-the-fold” contents. Then use a compressor (CSS Compressor) to help compress the CSS. Once done, copy it to the following box.

Autoptimize - defer CSS

If having question, you can check the Q&A page of Autoptimize

Optimize Images

Doing “image optimization” could be a totally new arena to website optimization. This post (Image optimization) gives us a comprehensive understanding & guide on how to do it.

Here is the “Image optimization checklist” from this post:

  • Prefer vector formats
  • Minify and compress SVG assets
  • Pick best raster image format
  • Experiment with optimal quality settings for raster formats
  • Remove unnecessary image metadata
  • Serve scaled images
  • Automate, automate, automate

For WordPress, we can just simply use some plugins to help achieve the basic outcome, which is still OK for most of the case.

Here are the plugins I’m using.

  • EWWW Image Optimizer – It can automatically optimize the image you upload to “Media”.
  • Regenerate Thumbnails – It helps you regenerate the thumbnails for images.
  • RICG Responsive Images – It helps add responsive images automatically by add srcset attribute.
  • WP Retina 2x – It helps create the image files required by the Retina (High DPI) devices and displays them to your visitors accordingly.

Check this post (RICG Responsive Images For WordPress) for more details of “RICG Responsive Images”.

Bug of W3 Total Cache with srcset

If you are using CDN provided by W3 Total Cache, one bug can be found when you are also using “RICG Responsive Images”.

If srcset has more than one image, W3 Total Cache will only replace the URL for the first image inside secretes such as:

<img class="img-responsive center-block wp-post-image" src="" sizes="(max-width: 890px) 100vw, 890px" srcset=" 252w, 860w, 202w, 403w, 645w, 890w" alt="ios-exceptions-cloud" width="890" height="1060" />

Hopefully W3 Total Cache is going to fix it quit soon.

Check here for more details.

Other Tweaks

Here are some other tweaks that I did to help improve the performance.

Remove the Jetpack Counter

If you are using Sharing of “Jetpack”, when you run test against “Pingdom Tools“, you will see a lot of HTTP requests like this:

This image is used for doing “sharing counts” on the button. To remove it, add the this line to functions.php:

add_filter( 'jetpack_sharing_counts', '__return_false' );

Add More Image Sizes

By default, WordPress comes with only 3 different image sizes which is not adequate. I added more possible image sizes by adding the following lines to functions.php.

add_action( 'after_setup_theme', 'pgb_add_responsive_image_sizes' );
function pgb_add_responsive_image_sizes() {
add_image_size( 'cropped-small', 320, 320, true );
add_image_size( 'cropped-medium', 640, 640, true );
add_image_size( 'responsive-small', 320, 240);
add_image_size( 'responsive-medium', 640, 480);
add_image_size( 'responsive-large', 1024, 768);

Start From Here

After done all the above practice, I can easily tell the performance of has been improved dramatically. The normal page load time is constantly controlled around 2 sec for the “initial load”. The “scores” on Google PageSpeed Insights, Pingdom Tools and GTmetrix are “fantastic”.

Google PageSpeed Insights Google Page Speed

Pingdom Tools Pingdom Test - Inside USA

GTmetrix GTmetrix Test

However, you should not be always chasing these “scores”. The WordPress is mainly built based on the Core WordPress as well as some plugins. It is highly possible that certain bug inside the plugins you choose that can drag down your scores.

Here are some examples of

  • “Eliminate render-blocking JavaScript and CSS in above-the-fold content” – caused by the file “” as well as a bug of “Autooptimize”.
  • “Prioritize visible content” – caused by having an image shown as “above-the-fold” contents. This can be done with Deferring Image.
  • “Leverage browser caching” – caused by “ (2 hours)”.

Hope this post can help you start to optimize your WordPress sites.


Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>