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

    if (!-e $request_filename) {
        rewrite /wp-admin$ $scheme://$host$uri/ permanent;     
        rewrite ^(/[^/]+)?(/wp-.*) $2 last;                    
        rewrite ^(/[^/]+)?(/.*\.php) $2 last;

    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/${host}${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; }

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:

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

  1. Hi,
    Thank you for all your tutorials.
    I tried to use this example and when i restart NGINX i receive this error message:

    nginx: [emerg] pcre_compile() failed: unrecognized character after (?< in "^(?/[_0-9a-zA-Z-]+/)files/(.*)” at “blogpath>/[_0-9a-zA-Z-]+/)files/(.*)” in /etc/nginx/conf.d/my.conf:2
    nginx: configuration file /etc/nginx/nginx.conf test failed

    • I tested again and I see config mentioned in this article is correct.

      Looks like you are running older version of PCRE lib.

      In line 2, add a “P” after “?” so that ~^(?<blogpath>/[_0-9a-zA-Z-]+/)files/(.*) will become ~^(?P<blogpath>/[_0-9a-zA-Z-]+/)files/(.*)

      You can also try upgrading libpcre and/or nginx.
      Also, please include output of nginx -V command if above fails to address your issue.

  2. Hi,
    I have an error when i try to enter the feed url when i use this script. i’m not sure that the this script is the problem, but i’ll appreciate if you can confim it.

      • the one in this page(mu+subdir+w3+nginx). i don;t know why but i get a blank page (http 200). don’t know where to start looking what is wrong. it worked well before.

        • If you see blank page on front-end but can access /wp-admin/ of your WordPress then most likely issue is with W3 Total Cache plugin.

          Try re-installing W3 Total Cache plugin. Make sure you manually delete cache folder and php files created in wp-content folder by W3 Total Cache plugin for a clean reinstall.

  3. Hi Rahul,
    admin is also blank, i tried to disable w3 and it didn’t help.
    i will try to remove it and come back with the results.

  4. @RahulBansal With this configuration, expires headers for the page (html) aren’t being set. Can you please check the config. used in the article? (Do let me know if I am missing something.)

    • We are not sending expires headers for page itself. It is sent for other static contents e.g. images.

      You can add a line in location /{} block to add expires header for page’s HTML output.

      It will look like:

      location / {
              try_files /wp-content/w3tc-$blog$host/pgcache$cache_uri/_index.html $uri $uri/ /index.php?$args ;
              expires 1h; 
  5. Outstanding tutorials, thanks Rahul.

    I’ve taken all the rules after root /... and put them in a separate file, (wordpress.conf) and then reference that file as an include /... from my various server {...} blocks after the server_name, log & ssl_certificate rules. This way I can use unique ssl certificates for specific domains, and reuse the rewrite rules to keep all the server blocks neat and tidy with just an include instead of having to copy the rules over and over again (either in each server block or in each /sites-available file). The end result looks something like this for each secure domains server block:

    server {
    listen 80;
    listen 443 ssl;
    server_name example.com;

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

    ssl_certificate /path/to/ssl/cert/example.com/example.crt;
    ssl_certificate_key /path/to/ssl/key/example.com/example.key;

    include /etc/nginx/wp/wordpress.conf;

    The setup seems to be working well, and I’m wondering if there’s anything wrong with using includes this way or if it’s taxing on the server, etc.

    Thanks again for all your stellar write-ups! They’ve been incredibly educational.

  6. Hi,
    tried to use this configuration but the site doesnt load, and i only see a blank screen. the admin acts the same.

    i noticed that w3 cache folder does not contain this sub folder:

    WP version is 3.5.1 and w3 version is

    Any idea?
    Thank you in advance

  7. When I use “Page Cache: Enhanced” on SSL sites, it breaks the CSS links to styles on my homepages, but not sub-pages. Any idea what’s up? (only HTTPS homepages, everything else seems fine)

  8. Hi! Thanks for these great posts! I have a question related with the nginx configuration when i use ‘Page Cache – Disk’ instead ‘Page Cache – Disk Enhanced’. I can’t use disk enhanced because i need to use mfunc (Page Cache – Disk Enhanced prevent caching when you use mfunc)
    So, I suppose that i should change this line:

    try_files /wp-content/cache/page_enhanced/${host}${cache_uri}_index.html $uri $uri/ /index.php?$args ;

    What is the right one?

    Thanks in advance!