Update: This article is updated for new W3-Total-Cache page-cache directory structure.
Assumption:
- You already installed PHP, MySQL, Nginx & Postfix
- You already installed a fresh WordPress or moved an existing WordPress to current server
Based on these assumptions, we will jump to directly a WordPress-Nginx configuration part.
Standard WordPress-Nginx configuration with W3 Total Cache:
W3 Total Cache plugin provides many options. I will recommend using the given below:
- Page Cache – Disk Enhanced
- Minify – Disabled (unless you are 100% sure your theme/plugin will support it nicely)
- Database Cache – Opcode: Alternative PHP Cache (APC)
- Object Cache - Opcode: Alternative PHP Cache (APC)
- Browser Cache – Disabled
- CDN – Your Choice (Need Help?)
If you have noticed, I have highlighted two options only. These are the only two options we are going to handle in our configuration.
server {
server_name example.com www.example.com;
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log debug;
root /var/www/example.com/htdocs;
index index.php;
set $cache_uri $request_uri;
# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
set $cache_uri 'null cache';
}
if ($query_string != "") {
set $cache_uri 'null cache';
}
# Don't cache uris containing the following segments
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
set $cache_uri 'null cache';
}
# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
set $cache_uri 'null cache';
}
# Use cached or actual file if they exists, otherwise pass request to WordPress
location / {
try_files /wp-content/cache/page_enhanced/${host}${cache_uri}_index.html $uri $uri/ /index.php?$args ;
}
location ~ ^/wp-content/cache/minify/[^/]+/(.*)$ {
try_files $uri /wp-content/plugins/w3-total-cache/pub/minify.php?file=$1;
}
location = /favicon.ico { log_not_found off; access_log off; }
location = /robots.txt { log_not_found off; access_log off; }
location ~ .php$ {
try_files $uri /index.php;
include fastcgi_params;
fastcgi_pass unix:/var/run/php5-fpm.sock;
}
# Cache static files for as long as possible
location ~* .(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
expires max; log_not_found off; access_log off;
}
}
Notes:
- To simplify configuration, I haven’t added Mobile user agent checks. Its better to use responsive design than maintaining a separate site for mobile devices.
- You may not see special rules for minify feature. Above rules can handle W3 total minification without any change.
Don’t Forget:
Always test your Nginx configuration and then reload it. All changes to Nginx config must be follow with these commands:
nginx -t && service nginx reload
Must Read:
- Checklist For Perfect WordPress-Nginx Setup - It will help you verify if your caching will work after PHP/MySQL crash.
- Catch the complete list of WordPress-Nginx tutorials here.


I wanted to feature this post in the W3TC readme, but i noticed that you don’t recommend enabling browser caching. Any reason why not?
Line
expires max;in Nginx configuration takes care of browser caching at nginx-level ver well. It sets cache duration for 25-years.I am using w3 total cache from long time. In fact, I wrote about Nginx+W3 total cache 2 years ago.
There is nothing wrong in “Browse Cache” option provided by your plugin. It’s just that nginx can handle it faster and itself. IMHO its always better to save PHP from some work. For this reason, I also recommend disabling gzip/compression related options.
By the way, I already scheduled a W3 Total Cache + Multisite article around weekend. That will also have few special configuration options.
Sorry, I think there’s some confusion. If you check the Install tab for example, you can see that W3TC generates the directives for your .conf file dynamically when you change policy (settings). So PHP isn’t actually doing anything in that regard, that would be non-performant. I hope that clears things up.
I have checked generated .conf files sometime back and I tried to cover everything in above configuration.
Does “Browser Caching” option is responsible for adding some rules into .conf file only?
I will check .conf file on my end again.
By the way, I just verified browser caching for this site by using following command:
curl -I http://rtcamp.com/files/2012/09/wordpress-nginx.jpegAs expected, Nginx returned correct Expires header in its output.
Expires: Thu, 31 Dec 2037 23:55:55 GMTPlease correct me if I am missing anything here.
The intent is for W3TC to generate Apache / Nginx rules that you can include into your main web server conf. So in this case the difference between using W3TC’s settings and doing it yourself is only that you can manage everything in one place by using W3TC. The W3TC is trying to take all of the policies you’re setting in the plugin and create the appropriate rules.
I have checked your nginx config again and above covers it all. I posted 3 more articles in this series for W3 Total Cache here.
Also for WordPress-multisite, there is a way to handle static /files/ without PHP & MySQL. You may add that feature to W3-Total-Cache in future.
There’s a trac ticket for WP to better handle files in an upcoming release, so I won’t be adding anything to W3TC.
Rahul,
have you tried benchmarking WP + Nginx + fastcgi_cache versus WP + Nginx + W3TC? What are your thoughts and preferred setup between the two?
I have been using WP + Nginx + fastcgi_cache flawlessly after reading your tutorial, but wonder if I would have any performance gain with W3TC instead.
Keep up the great work!
@Resende
Glad to know that you found this useful.
I haven’t done any benchmarking yet, but once a page is cached, fastcgi_cache and W3 Total Cache may not differ much. Once a page is cached Nginx alone can serve it, assuming cache is properly configured.
Technically, fastcgi_cache will have upper age as:
For a cache-miss, definitely fastcgi_cache method will be faster as PHP-process won’t be executing any codes for cache-generation.
These days, I am running some benchmarks on a client server. I will ask them if I can run fastcgi_cache v/s W3 Total Cache benchmark on their server.
Thanks for these great tutorials!
Have you done any of the benchmarks yet? I would be really interested in seeing Nginx + APC with W3 Total Cache versus the same with Wp Super Cache and if I should be disabling fastcgi_cache when using either of these?
Various caching types should be compared unless your goal is to learn how they are different. Memory caching is not always “faster” than disk caching, the problem you’re solving and how you’re measuring / testing determine what you will learn even with all the same testing environment.
It’s probably a dumb question, but where is the file I would write the above server { } to? Thank you in advance.
For ease-of-maintenance we follow certain conventions as outlined in this article – http://rtcamp.com/tutorials/conventions-file-system-layout-for-ideal-wordpress-nginx-setup/
So I will recommend you to create a file-like
/etc/nginx/sites-available/example.comto put above config in it.I want to know is there any way to use minify on CSS and JS while using fastcgi_cache alone..Minify is one of the feature in w3 total cache..(I used w3 total cache with fastcgi_cache and just removed the plugin).
I know there is a pagespeed module for nginx but that is not production ready?? Looking forward for your suggestion on this issue.
I will recommend using http://wordpress.org/extend/plugins/wp-minify/ for CSS & JS minification.
I am testing pagespeed module for Nginx but it will take some time before I can say anything about it. Most likely I will publish an article on pagespeed + nginx as soon as I get it working properly. You may subscribe here to get updates.
Thanks Rahul for speedy response.In the meantime I checked you are using W3 total cache on this site (no fastcgi_cache if I am right)..is there are specific reason for it..I saw in another post you recommended fastcgi_cache + nginx over nginx + w3 and nginx + wp super cache.
We are using Nginx’s fastcgi_cache for page-caching here. W3 Total Cache is used for only MySQL & Object cache.
I recommend Nginx’s fastcgi_cache because I think its faster. I do not have any benchmark data to support it though.
You can use any page-cache. As long as cache can serve pages with PHP/MYSQL crashed, there is no issue with any cache.
where do I put
server {
server_name example.com http://www.example.com; ….
}
}
Note: shortened to not put the whole code
I’m using IspConfig3, would be nginx Directives?
I use a $5 VPS with 512MB RAM, 1 Core and 20 GB SSD. Despite the fact that it’s SSD, and my Nginx configuration is as optimized as yours (and thank you for all these guides
, I figured that I’d give memcached a try for Page Cache option of W3 Total Cache.
Here’s the Blitz report when I use the same W3 configuration as you’ve mentioned in this post:
https://www.blitz.io/report/2cc5c638f7c471adbe42f5c099572c84
And here’s the report after I switched Page Caching using Memcached:
https://www.blitz.io/report/2cc5c638f7c471adbe42f5c0995723f6
I monitored CPU usage (poor 1 core VPS) during the Blitz Rush – using Memcached allowed a constant <5% CPU occupation (combination of php-fpm worker and nginx worker mostly), while using Disk page cache resulted a constant 100% after concurrency reaches about 150.
Just my two cents
Thanks for your time and efforts.
However, there are many things which are not clear from your reports.
For first report, did you turn on pagecaching using Disk Enhanced method as I suggested in article?
Have you ran tests in reverse order? Memcached first and disk-cache second. Did you see any difference?
Have you verified that in disk-based config, cache was configured correctly? Have a look at this checklist. 100% CPU utilisation might be because of for every pageview, cached version of page getting regenerated.
Off-topic, if its possible for you, can you use config presented here – http://rtcamp.com/tutorials/nginx-wordpress-fastcgi_cache-with-conditional-purging/ ?. In other config, we use disk-based caching only but rather than HDD, its a RAMDISK (tmpfs) in action.
Aren’t you missing the host in the rewrite for cached pages? Request_uri doesn’t include the hostname but the cache folder is in page_enhanced then host the uri subfolder.
@Bryan
Thanks for pointing it out.
I updated other W3 Total Cache related articles few days back. Forgot to update this one before.
Updated now. Thanks again!
I’d also recommend adding a rewrite for ?replytocom=* and ?utm_source*. These circumvent caching but should be rewritten to the URI (no args) or served from cache. Other wise bots our hackers blow up the load since the query string doesn’t hit the cach but php/mysql dierectly. Also a good idea to avoid “dup” content.