W3 Total Cache + (optional) Domain Mapping

Update: This article is updated for WordPress 3.5 multisite’s file-handling and new W3-Total-Cache page-cache directory structure.

easyengine (ee) note: If you are using easyengine, you can accomplish everything in this article using following command:

ee site create example.com --wpsubdom --w3tc

In this article, we will setup WordPress Multisite using subdomains with Nginx with added support for W3 Total Cache Plugin.

If you haven’t created a WordPress Multisite network, please check this guide first.

Nginx Config

Below is recommended Nginx configuration.

server {
        ##DM - uncomment following line for domain mapping  
        #listen 80 default_server;
    server_name example.com *.example.com ;
    ##DM - uncomment following line for domain mapping
    #server_name_in_redirect off;

    access_log   /var/log/nginx/example.com.access.log;
    error_log    /var/log/nginx/example.com.error.log;

    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';
    if ($query_string != "") {
        set $cache_uri 'NULL';

    # 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';

    # 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';

    # Use cached or actual file if they exists, otherwise pass request to WordPress
    location / {
        try_files /wp-content/cache/page_enhanced${cache_uri}_index.html $uri $uri/ /index.php?$args;

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;

    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 max;

    location = /robots.txt { access_log off; log_not_found off; }
    location ~ /\. { deny  all; access_log off; log_not_found off; }
    location ^~ /wp-content/w3tc- { deny  all; access_log off; log_not_found off; }

Above configuration is already tested for minification & browser caching. You can disable minification in  W3-Total-Cache settings.

Domain Mapping

You need to uncomment few lines in above nginx-config to get domain-mapping working. Apart from above config changes, you can read this guide to setup/configure domain-mapping.

Must Read:

15 responses to “W3 Total Cache + (optional) Domain Mapping”

  1. Hello,

    With wordpress 3.5 comes some pretty drastic changes with media file handling. More specifically, the Upload file path has changed completely in 3.5 as can be found here: http://codex.wordpress.org/Multisite_Network_Administration#Uploaded_File_Path

    Is there any chance of you updating your guides to reflect these changes? I’m extremely new to nginx and would hate to have to go back to apache or use an older version of wordpress because of not knowing nginx well enough to adapt.

    Thanks for your consideration.

  2. For domain-mapping with A-records, you need to make few changes.
    Please check commented line in following configuration.

    Which line were you referring to? Why would my nginx conf change if I’m using an A record vs. a CNAME?

    Thanks, great articles!

    • I am referring to line:

      #server_name_in_redirect off;

      By default it is off but in case you face any issue, you need to uncomment that line. More info about server_name_in_redirect is here.

      Regarding A-records v/s CNAME…

      First, there was a typo. I wanted to say that you need to make changes for domain-mapping to work. The change was a line as mentioned above. So domain-mapping may affect your config. Not sure if you need to change config while switching from A-records to CNAME.

      If you are using CNAME records + domain-mapping, can you please share your nginx config?

      I never used CNAME records as they involve 2 lookups but there are valid cases to use CNAME records.

      Anyway, I corrected line which caused confusion. Thanks.

  3. I’m having some issues since I can’t use the same vps for hosting other websites, as soon as I create another “virtual server” on nginx*, WordPress MU + Domain Mapping stops working (500 error).

    I’m really confused because if I set it, the new server works, but everything else, including all the others domain that I’m managing trough the DM plugin, also stops working.

    *something being really simple like:

    server {
    server_name a_different_domain.tld;
    root /some/other/place;

    try_files $uri $uri/ /index.php$is_args?args;

    location ~ \.php$ {
    include fastcgi.conf;
    fastcgi_passs unix:/var/run/php-fpm/another_socket.sock;

    My original nginx.conf (the one that’s hosting the network) is similar:

    server {
    server_name mydomain.tld *.mydomain.tld;
    server_name_in_redirect off;
    root /some/place;

    try_files $uri $uri/ /index.php$is_args?args;

    include file_with_confs.conf;

    location ~ \.php$ {
    include fastcgi.conf;
    fastcgi_passs unix:/var/run/php-fpm/my_socket.sock;

  4. I’m having a problem with a similar setup!

    Everything worsks fine in the Primary Blog, but all others give me 404 on CSS files, even the Network site!

    What could I be missing?

      • I found the problem, it was actually all static files, including pics, because those blogs are using the /uploads dir, not “/files”, the css was my misconfiguration… I was using hotlink protection and forgot to add the 2 of the blogs, to the allowed referrers!

        Btw do you have any hints on SSL w/ W3 Total Cache? That’s my next step… The idea would be to rewrite all to SSL… not sure if this part still works though:

        # Use cached or actual file if they exists, otherwise pass request to WordPress
        location / {
        try_files /wp-content/cache/page_enhanced${cache_uri}_index.html $uri $uri/ /index.php?$args;

  5. I used easy engine to install multisite subdomain with w3 and thinks it works as expected.

    What I would want to accomplish now is that when someone visits farak.in with A record pointing to the wordpress VPS, then url in the address bar does NOT change from farak.in to farak.sachinkundu.name. I think the default behavior is not to change the address but there must be some rewrite rule which I am not able to find.

    The response headers returned when visiting farak.in is

    Request URL:http://farak.in/
    Request Method:GET
    Status Code:301 Moved Permanently

    Request Headers
    User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.72 Safari/537.36

    Response Headers
    Content-Type:text/html; charset=UTF-8
    Date:Wed, 15 Jan 2014 09:09:11 GMT

    • I think you want farak.sachinkundu.name and farak.in both to return farak.sachinkundu.name in address bar.

      In domain-mapping plugin there is a “Primary” checkbox. It might work on your end.

      Other way is to create a nginx config for farak.in on server and redirect all requests to that domain to farak.sachinkundu.name

  6. A tiny comment on the fact that the server directive needs to be included in the http directive of the config file.

    Also, I am now getting 500 server errors for any files that I have attached to the site which do not map properly. This happened with a logo that I set in the theme preferences.

    The URL to that logo looks like http://subdomain.mainsite.com but when I preview the site I am mapped to mappeddomain.com which cannot load the http://subdomain.mainsite.com/files/logo.png. Is there anything I may be overlooking?