fastcgi_cache + conditional purging + (optional) Domain-Mapping

Update: This article is updated for WordPress 3.5 multisite’s file-handling.

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

ee site create example.com --wpsubdir --wpfc

This article is direct extension of yesterday’s article: WordPress + Nginx + fastcgi_cache with fastcgi_cache_purge module.

Please read that article to check if Nginx is complied withfastcgi_cache_purge module and help on installing Nginx Helper plugin.

WordPress-Multisite (Subdirectories) + Nginx + fastcgi_cache with fastcgi_cache_purge module

Make changes to /etc/nginx/sites-available/example.com file so it looks like one below:

#move next 3 lines to /etc/nginx/nginx.conf if you want to use fastcgi_cache across many sites 
fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:500m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;

server {
    #@DM - uncomment following line for domain mapping or you will need to add every mapped-domain to server_name list 
    #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;

    #fastcgi_cache start
    set $skip_cache 0;

    # POST requests and urls with a query string should always go to PHP
    if ($request_method = POST) {
        set $skip_cache 1;
    }   
    if ($query_string != "") {
        set $skip_cache 1;
    }   

    # 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 $skip_cache 1;
    }   

    # Don't use the cache for logged in users or recent commenters
    if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
        set $skip_cache 1;
    }

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

    location / {
        try_files $uri $uri/ /index.php?$args;
    }    

    location ~ \.php$ {
        try_files $uri =404; 
        include fastcgi_params;
        fastcgi_pass 127.0.0.1:9000;

        fastcgi_cache_bypass $skip_cache;
            fastcgi_no_cache $skip_cache;

        fastcgi_cache WORDPRESS;
        fastcgi_cache_valid  60m;
    }

    location ~ /purge(/.*) {
        fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
    }   

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

The line fastcgi_cache_use_stale is what makes caching on Nginx-side unique. This line tells nginx to use old (stale) cached version of page if PHP crashes. This is something not possible with WordPress caching plugins.

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 “fastcgi_cache + conditional purging + (optional) Domain-Mapping”

  1. nginx: [emerg] pcre_compile() failed: unrecognized character after (? or (?- in “^(?/[_0-9a-zA-Z-]+/)files/(.*)” at “/[_0-9a-zA-Z-]+/)files/(.*)” in /etc/nginx/sites-enabled/default:7
    nginx: configuration file /etc/nginx/nginx.conf test failed

    Getting this error message on test. Something wrong here?

    • Looks like a PCRE version issue.

      On Ubuntu, try: apt-get install libpcre3 libpcre3-dev

      Another workaround is to use following line instead:

      ~^(?P/[_0-9a-zA-Z-]+/)files/(.*)       $blogpath ;
    • I use “/purge” from browser sometime to manually purge cache for a WordPress URL (post/page/archive etc).

      You can restrict it to localhost if you do not wish to use “/purge” manually.

  2. Is this compatible with W3 Total Cache? I’d like to use NGINX caching, but I also have W3 and Memcached.

  3. Hi Rahul.
    I installed this WP settings with easy engine:

    ee site create wpsubdir wpfc mydomain.com

    but I noticed that this lines of code were not added:

    fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:500m inactive=60m;
    fastcgi_cache_key “$scheme$request_method$host$request_uri”;
    fastcgi_cache_use_stale error timeout invalid_header http_500;

    Should I adding it manually?
    Yoy say “use_stale” makes the difference, thats what I think It should be there.

    • Those lines are added only once in /etc/nginx/conf.d/fastcgi.conf and reused again and again.

      About “use_stale”, that is handled by line fastcgi_cache_use_stale

  4. Hi Rahul, it’s been a while. Hope you are doing fine! 🙂

    I’m going to ‘re-use’ a site that was running a subdomain install before for a new subdirectory install. Can you confirm that the only (essential) difference in the Nginx config files between subdir and subdomain intallations is this part?

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

    Or am I missing something else that will come back to haunt me later?

    Thanks for you insights. 🙂

    • I am fine. 🙂
      Hope you are fine as well.

      subdomain and domain are same thing from config perspective.

      for subdir, if its wordpress site you can try adding:

      location /dir {
      	try_files $uri $uri/ /dir/index.php?$args;
      }
      

      If you many sites in different subdir (top-level subdirs), you can try:

      	set $dir1 "NULL";
      	if ($request_uri ~ ^/([^/]*)/.*$ ) {
      		set $dir1 /$1;
      	}
      	
      	location / {
      		try_files $uri  $uri/  $dir1/index.php?$args ; 
      	}
      
    • By the way if you are trying to copy “nginx config” from multisite-subdomain to multisite-subdir, then there will be better to use config from above article and change directory paths e.g. root.

      • Hi Rahul, yes it’s a multisite and it seems to be working fine with the above config except for one thing:

        I created a second multisite (with another root) and moved the fastcgi_cache block to the main nginx.conf. On the first multisite caching is still working fine but on that second site, cache is not working. In the response header I always get “Fast-Cgi: MISS”…

        The two sites configs are exactly the same except for root, server_name (obviously) and log locations. I cannot for the life of me figure out why this is happening. If it would be something like request variables or cookies the I’d see “Fast-Cgi: BYPASS”, right? IIf I log in or do a search on the second multisite I get BYPASS. But I never see HIT.

        Whatever could be happening?

        • Update: I found the culprit. It was the MarketPress (e-commerce) plugin that I had activated Network wide. The plugin has a start_session() in the main class __constuct() method… Looks like that will make fastcgi_cache back down, is that correct?

          Is there any way around that? The plugin sets a cookie that starts with ‘mp_globalcart_’ as soon as something is added to the cart. It would be nice to keep caching active until that moment.

          Thanks for any tips 🙂