WordPress Nginx

Home WordPress Nginx Tutorials Single Site fastcgi_cache with conditional purging

fastcgi_cache with conditional purging

In first part of this series, we have seen many combinations of different WordPress setup with different caching plugins. Today, we will use an altogether different way of caching!

Rather than asking a complex PHP-MySQL application like WordPress to do some extra work for caching, we will ask light-weight Nginx to cache WordPress content on its end.

Nginx has built-in support forfastcgi_cache but it doesn’t support conditional cache purge out of the box. Conditional cache purging is needed to rebuild cached content after you edit a post/page from WordPress dashboard or approve a comment on an article.

Install Nginx with fastcgi_cache_purge module

First, check if your nginx already supports this by running command

nginx -V 2>&1 | grep nginx-cache-purge -o

If you see nginx-cache-purge in output then you already have it. Otherwise run following commands to install nginx with fastcgi_cache_purge module.

sudo add-apt-repository ppa:brianmercer/nginx
sudo apt-get update
sudo apt-get install nginx-custom

Install Nginx Helper Plugin

Above step ensures that Nginx can purge a page from its fastcgi_cache selectively. But Nginx cannot automatically find out which page to purge and when to purge?

So install Nginx helper plugin from WordPress plugin repository and activate it. Apart from other features, it provides cache purging options. Just activate it, go to its settings and turn on “Enable Cache Purge” option.

If you want more control over your cache purging rules, you can play with different purging options it provides.

Nginx Configuration for WordPress with fastcgi_cache_purge support

No matter how you are using WordPress, i.e. single or Multisite (with subdirectory/subdomain/domain-mapping)fastcgi_cache related configuration will remain similar.

Use ramdisk (tmpfs) to store cached content

You need give Nginx a folder  storefastcgi_cache content. I will recommend using /var/run on Ubuntu as its mounted as tmpfs (in RAM). If you do not have ample RAM you can pick any other location.

If you are going with RAM, make sure you check size of /var/run folder. Its generally 20% of your total RAM size.

To verify it, run this command: df -h /var/run

You will see output like below:

Filesystem      Size  Used Avail Use% Mounted on
tmpfs           6.3G  364K  6.3G   1% /run

It looks like we have more than 6GB at our disposal! Our server has 32GB RAM by the way, so 6GB is close to 20%

Nginx Configuration for exmaple.com

Now 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 {
	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;

	root /var/www/example.com/htdocs;
	index index.php;

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

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

	location ~ .php$ {
		try_files $uri /index.php; 
		include fastcgi_params;
		fastcgi_pass unix:/var/run/php5-fpm.sock;

		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.

Don’t Forget:

Always test your Nginx configuration and then reload it. All changes to Nginx config must be followed with these commands:

nginx -t && service nginx reload

Must Read:

For WordPress Multisite, you can either make necessary changes by following this guide or check my next posts.

82 Comments…

 Share your views
  1. Wow! Nice work! This is the first complete how-to and configuration that really works well :-)

    I do have 1 request. If I’m not mistaken, the cache of a post does not get purged when you edit a post. I think this rather essential (the homepage does get purged).

  2. Avatar of Rahul Bansal

    @Greg

    Please use support forum instead. Solution to your issue may help others as well.

    Don’t worry. Nginx-Helper doesn’t log any sensitive information.

  3. Thank you for this article. However when I try to install nginx-custom, I receive the following error:

    The following packages have unmet dependencies:
    nginx-custom : Depends: nginx-common (= 1.2.4-1ppa4~precise) but 1.2.4-2ubuntu0ppa1~precise is to be installed
    E: Unable to correct problems, you have held broken packages.

    Does this mean the repo is not updated for my version of nginx?

    • Avatar of Rahul Bansal

      @Richard

      I have tested everything on Ubuntu 12.04 (precise) which is same as yours.

      Try running following commands:

      nginx_installed=`dpkg -l | grep nginx | awk '{print $2}' | tr "\n" " "`
      sudo apt-get purge $php_installed
      sudo add-apt-repository ppa:brianmercer/nginx
      sudo apt-get update
      sudo apt-get install nginx-custom
      

      Let me know if above works for you.

      • Unfortunaltely I receive the same error message as before. I followed the http://rtcamp.com/tutorials/installing-phpapc-mysql-postfix-nginx-and-wordpress-on-ubuntu/ tutorial before this one. Could it be that the nginx-stable repository from that tutorial is conflicting with the brianmercer from this one?

        • Avatar of Rahul Bansal

          Could it be that the nginx-stable repository from that tutorial is conflicting with the brianmercer from this one?

          Most likely yes! Please remove ppa:nginx/stable from /etc/apt/sources.list.d and try again. Don’t forget to run sudo apt-get update in between.

          Just for info, on our server, I have ppa:nginx/stable and ppa:brianmercer/nginx both coexisting together.

          Alternatively, packages from here http://www.dotdeb.org/instructions/ will also work (most likely)

          • I’ve removed the stable from sources, ran the apt-get update and tried installing nginx-custom, but still no luck. Think I will just start from scratch with a fresh install of my server. I was wondering, would you recommend using fast-cgi caching over W3TC, performance wise?

            Also, if I may suggest a future tutorial, it would be about configuring linux for sftp or scp use. Specifically on setting the correct group/user permissions for a root user to add/modify files and folders using filezilla or winscp, while still maintaining ownership to www-data. I’ve tried multiple tutorials found online, but none of them seem to work for me. Anyway, thanks for your help and providing these ningx tutorials to the community. Much appreciated!

          • Avatar of Rahul Bansal

            @Richard

            I have done some benchmarks but its hard to conclude either way.

            Once a page gets cached, nginx does all the work. So there is not much performance difference for “reading/serving” a cached page.

            In theory, W3TC cached pages will be served faster when small number of pages are cached. For large sites, where 10000′s of pages are cached, fastcgi might be faster.

            In terms of “creating/generating” a cached page, I feel nginx-fastcgi will take less resources.

            Personally, I am using nginx-fastcgi for page-cache everywhere. I also use W3TC for Object-Cache, Database-Cache & CDN.

            ==

            Regarding SFTP, we login with www-data user to edit files. So we never faced any permission issue.

  4. Excellent article, thank you.

    Do you know if it is possible to use a free control panel with the above configuration?

    Thank you.

  5. Hi, In order for this line to work

    sudo add-apt-repository ppa:brianmercer/nginx
    

    A Python module had to be installed first on Ubuntu like this:

    sudo apt-get install python-software-properties -y
    

    Without installing that, the command `add-apt-repository1 came back as not being recognized Do you have a repository to use for Ubuntu 10.10? since the one you recommend is for 12.04 which my host (Rackspace) has some problem with.

    Thanks, Nick

  6. Hi, I did. Created a new virtual server with 12.04 followed your instructions from step 1. Now I am on this step and the following line:

    sudo apt-get install nginx-custom
    

    returns back the following:

    Reading package lists... Done
    Building dependency tree
    Reading state information... Done
    Some packages could not be installed. This may mean that you have
    requested an impossible situation or if you are using the unstable
    distribution that some required packages have not yet been created
    or been moved out of Incoming.
    The following information may help to resolve the situation:
    
    The following packages have unmet dependencies:
     nginx-custom : Depends: nginx-common (= 1.2.4-1ppa4~precise) but 1.2.5-1~dotdeb.0 is to be installed
    E: Unable to correct problems, you have held broken packages.
    

    Any ideas what to do? I tried every other suggestion written above on this page? Thanks, NIck

  7. Hi ,
    I implemented it the way as described in tutorial but faced serious performance issue , I can see in configuration index.php is not getting cached ? is it so ? Can we enable caching for index.php ? Will the nginx helper plugin will work with it then.

  8. Hi,

    When I try to install, I get the following error:

    E: Unable to locate package nginx-custom

    Any ideas?

    • Avatar of Rahul Bansal

      @Joe

      Sorry for delayed reply. I am traveling from last 2-3 days.

      Did u forget to run apt-get update after adding sudo add-apt-repository ppa:brianmercer/nginx?

      Please paste here output of command: apt-cache showpkg nginx. It might help me debugging your issue.

  9. Actualli I did the installation in centos , I recompiled the module with nginx it is the latest stable version so i dont think their is no problem with the package but it may be with the configuration please find below config -

    
    fastcgi_cache_path /data1/nginx-cache1 levels=1:2 keys_zone=WORDPRESS:500m inactive=420m ;
    fastcgi_cache_key "$scheme$request_method$host$request_uri";
    fastcgi_cache_use_stale error timeout invalid_header http_500;
    
    server {
        listen     0.0.0.0:80;                # your server's public IP address
        server_name  http://www.cannotdisclose.com ;                   # your domain name
        index index.php index.html ;
        root         /data1/www/localhost.com/public_html/;  # absolute path to your WordPress installation
        set $no_cache 0;
        try_files $uri $uri/ /index.php;
    
    location ~ .php$ {
    
    #       include        fastcgi_params;
      #      fastcgi_pass   unix:/var/run/php-fcgi.sock;
       #     fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    
    }
    
    set $no_cache 0;
    add_header    Cache-Control  public;
    expires       modified +10m;
    
    # POST requests and urls with a query string should always go to PHP
        if ($request_method = POST) {
                set $no_cache 1;
        }
        if ($query_string != "") {
                set $no_cache 1;
        }
    
        # Don't cache uris containing the following segments
        if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|registe                                      r|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 $no_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 $no_cache 1;
        }
    
        location / {
                try_files $uri $uri/ /index.php?$args;
        }
    
    location ~ .php$ {
                    try_files $uri /index.php;
                    include fastcgi_params;
    
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    
    fastcgi_pass   unix:/var/run/php-fcgi.sock;
    
    fastcgi_pass   127.0.0.1:9000;
            #fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
       fastcgi_param SCRIPT_FILENAME $request_filename;
    
                fastcgi_cache_bypass $no_cache;
                fastcgi_no_cache $no_cache;
    
                fastcgi_cache WORDPRESS;
                fastcgi_cache_valid  420m;
        }
    
        location ~ /purge(/.*) {
            fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
        }
    
        location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpe                                      g|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 = /favicon.php { access_log off; log_not_found off; }
        location = /robots.txt { access_log off; log_not_found off; }
        location ~ /\. { deny  all; access_log off; log_not_found off; }
    }
    

    Q1 : What does the below directive mean
    fastcgi_cache_path /data1/nginx-cache1 levels=1:2 keys_zone=WORDPRESS:500m inactive=420m;

    is 500m in MBs or minutes ?what is the ideal value for it and for inactive ?

    Q2 : fastcgi_cache_valid 420m;

    What does this directive mean ? is it in MB or minutes ? what is the ideal value , Our aim is to keep the cache for many days and it should refresh only when some does a new posting .

    Q3: Is supercache in nginx better then Nginx with Fastcgi cache ? I tried varnish but had lot of problem ?

    Help will be appreciated as we have around million hits in a day and during peak loads we get very high load on Mysql and PHP-FPm process.

    Thanks ,
    Abhishek

    • Avatar of Rahul Bansal

      I see many inconsistencies in your config so for config part, please refer to my post above.

      Regarding your questions:

      A1. 500m is MB (size). You can find details about parameter here

      Ideal value depends on your setup and requirement. We use inactive = 10m which is default. If load on server increases in future, we won’t mind using higher value.

      A2. 420m is minute as its duration value. You can find details about parameter here

      A3. I like nginx fastcgi-cache. Its bit complex to maintain but I enjoy using it. I am not sure about benchmarks but in theory, fastcgi-cache should perform better on large sites (1000s of cached pages)

      For any site, you can tweak things at many level. Start with http://gtmetrix.com/ and see how your site scores. Optimize by following gtmetrix.com suggestion first. Then you can move over to Nginx, PHP-FPM and also mysql-optimization.

  10. Thanks for the response , Can we cache index.php , In tutorial i can see you are not caching WordPress index.php , Will nginx nginx helper plugin work with it ?

  11. Avatar of ovidiu

    I’ve got a question concerning this line: fastcgi_cache_path /var/run/nginx-cache

    What ownership/permission does that folder need to have? It seems nginx created it automatically like this:

    drwx—— 2 www-data root 4096 Jan 11 07:06 nginx-cache

    • Avatar of Rahul Bansal

      @Ovidiu

      Nginx automatically creates the required cache directory.

      On our server also, we have same ownership & permissions:

      drwx—— 18 www-data root 360 Jan 9 01:38 nginx-cache

      I did not face any issue with that. Are you facing any problem with that?

  12. @RahulBansal

    1. From your comments above, I understand that you use nginx + fastcgi_cache for this site’s page caching, which implies that cached, static versions of the dynamic pages are served by Nginx, correct?

    In that case, what’s the need for Object-Cache & Database-Cache using W3 Total Cache? The user requests don’t reach the database anyway, so, why?

    2. Is this meant for a single server setup only? or does it work just fine on a setup with multiple load-balanced servers too?

    Great job with the tutorials by the way!

    • Avatar of Rahul Bansal

      @Aahan

      #1. Object-Cache & Database-Cache will speed-up “write” to cache. On a busy site where cache-needs to be flushed periodically, this may provide significant performance improvement.

      Also, on our network, we have support-forum, product-store and a community-blog. Together, they keep more than 50 users logged-in anytime. For logged-in user we do not use page-cache so object/database cache helps speed-up page-loading significantly.

      Finally, on any site, wp-admin (backend) will surely benefit from presence of object/database cache.

      #2. I haven’t tested this setup with load-balancer so this is tested with single-server setup only. Multiple load-balancers can be configured in many-ways so its hard to cover them here. Only thing I will suggest is to go with memcache in multiple-server setup.

  13. Oh, I missed this earlier.

    How does this kind of caching handle widgets like “Latest Posts” or “Recent Comments” in sidebar on Post pages?

    • Here are some examples as to what I mean:

      1. Infinite scrolling on Post pages showing the latest posts (among other things).

      2. Latest posts in sidebar with infinite scrolling.

      So, the question (again) is, how’s caching of such features handled in this method?

      • Avatar of Rahul Bansal

        I am not sure how Mashable/ReadWrite guys are doing it but if you check browsers’ developer-console, you will see AJAX requests are made to fetch extra content from server.

        For example, I saw request to a URL – http://readwrite.com/_ajax/article-list?page=3&count=10 on ReadWrite.com

        I am not sure if calls to /_ajax/article-list are routed to WordPress. Assuming they are routed to WordPress, object/database-level caching will directly reduce PHP’s work.

        You can tweak Nginx config also. Basically, you can ask Nginx to cache output of PHP if request_uri starts with “/_ajax/”. Also you need to tell nginx to use fastcgi_cache even if query string is present (page, count vars in above URL).

        Make sure you adjust “fastcgi_cache_key” to include $args in Nginx config.

    • Avatar of Rahul Bansal

      In our config => if browser sends a GET request (without query variable) to Nginx server, Nginx caches entire page-output it receives from PHP backend.

      Things like “Latest Posts”, “Recent Comments” etc are not known to Nginx. Its WordPress things and falls under PHP-mysql business! This is where object/database-caching kicks in. For second-page, PHP has to do less work because it can find result for “Latest Posts”, “Recent Comments”, etc in object/database-cache. For this reason, I always recommend using object/database-caching no matter which type of page-caching is being used (or even if page-cache is not used at all)

  14. Awesome tutorial, thanks for putting it together. I’ve setup three WordPress installations under a single domain:

    http://www.mainsite.com, http://www.mainsite.com/blog1, http://www.mainsite.com/blog2

    They all serve pages correctly, however only the two WordPress installations in subdirectories correctly cache for non-logged in visitors (registered users don’t see cache ever, as expected). In other words, http://www.mainsite.com/blog1/a-random-post will serve a nginx-cached page (timestamp from half an hour ago, for example, reloading does not change timestamp), but http://www.mainsite.com/another-random-post generates a new page every time it’s accessed (timestamp updates with every reload).

    Any clue why this would be happening? Any help would be greatly appreciated.

    Here is the fastcgi part of my nginx config file:

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

    Here is my domain specific config file:

    
    server {
            listen 80;
            #listen [::]:80 default ipv6only=on;
    
            server_name http://www.mainsite.com mainsite.com;
            root /home/mainsite/domains/mainsite.com/public_html;
            access_log /home/mainsite/domains/mainsite.com/logs/access.log;
            error_log /home/mainsite/domains/mainsite.com/logs/error.log;
    
            index index.php index.html index.htm;
            error_page 404 /404.html;
    
            set $no_cache 0;
    
            # POST requests and urls with a query string should always go to PHP
            if ($request_method = POST) {
                    set $no_cache 1;
            }
            if ($query_string != "") {
                    set $no_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 $no_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 $no_cache 1;
            }
    
            location / {
                try_files $uri $uri/ /index.php$args;
            }
    
            location /blog1 {
                try_files $uri $uri/ /blog1/index.php?$args;
            }
    
            location /blog2 {
                try_files $uri $uri/ /blog2/index.php?$args;
            }
    
            location ~ .php$ {
                    try_files $uri /index.php;
                    include fastcgi_params;
                    fastcgi_pass unix:/var/run/php5-fpm-mainsite.sock;
                    fastcgi_cache_bypass $no_cache;
                    fastcgi_no_cache $no_cache;
    
                    fastcgi_cache WORDPRESS;
                    fastcgi_cache_valid  180m;
            }
    
            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 = /favicon.php { access_log off; log_not_found off; }
            location = /robots.txt { access_log off; log_not_found off; }
            location ~ /\. { deny  all; access_log off; log_not_found off; }
    
    }
    

    Thanks again.

    • Avatar of Rahul Bansal

      We have a similar setup like yours on our demo server.

      Rather than following…

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

      We are using…

      
             set $dir "";
             
             if ($request_uri ~ ^/([^/]*)/.*$ ) {
      	       set $dir1 /$1;
             }
      
             location / {
      	       try_files $uri $uri/  $dir1/index.php?$args;
             }
      

      Try above change to see if it works!

      • Thanks so much for your quick reply. I made the change in the domain specific config file. Unfortunately, I’m still seeing the same issue.

        In a clean browser the two WordPress installs in subdirectories, http://www.mainsite.com/blog1 and http://www.mainsite.com/blog2, run as expected, they cache correctly (create a cache file and access it on subsequent page loads). However, the WordPress install in the root directory, http://www.mainsite.com, does not cache at all. Refreshing the page results in footers like this:

        and a few seconds later:

        The WordPress install in the root directory never accesses the cache, but seems to be generating new cache files continually, whereas the WordPress installs in the subdirectories, http://www.mainsite.com/blog1 and http://www.mainsite.com/blog2, cache a page and show in the footer that they’re accessing caches from 10, 20, 60 minutes ago, etc.

        Any idea what might be happening?

        Thanks again for the help.

        • Avatar of Rahul Bansal

          Try adding following line:

          fastcgi_ignore_headers Cache-Control Expires Set-Cookie

          After line:

          fastcgi_cache_use_stale error timeout invalid_header http_500;

          Do not forget to reload nginx config. Let me know if it works?

          Important: When you comment out extra lines for blogs in subdirectories, does it activates caching for main-site (in root-dir)?

          • Try adding following line:

            fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

            Bingo! I had tried that before, I thought, but it works perfectly now.

            Thank you so much. :)

            For reference for anyone in a similar situation, here is the cache section of my working nginx.conf:

            # fastcgi cache config
            fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:100m inactive=180m;
            fastcgi_cache_key “$scheme$request_method$host$request_uri”;
            fastcgi_cache_use_stale error timeout invalid_header http_500;
            fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

            And here is the domain specific conf file:

            server {
            listen 80;
            #listen [::]:80 default ipv6only=on;

            server_name http://www.mainsite.com mainsite.com;
            root /home/mainsite/domains/mainsite.com/public_html;
            access_log /home/mainsite/domains/mainsite.com/logs/access.log;
            error_log /home/mainsite/domains/mainsite.com/logs/error.log;

            index index.php;
            error_page 404 /404.html;

            set $no_cache 0;

            # POST requests and urls with a query string should always go to PHP
            if ($request_method = POST) {
            set $no_cache 1;
            }
            if ($query_string != “”) {
            set $no_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 $no_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 $no_cache 1;
            }

            set $dir “”;

            if ($request_uri ~ ^/([^/]*)/.*$ ) {
            set $dir1 /$1;
            }

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

            location ~ .php$ {
            try_files $uri /index.php;
            include fastcgi_params;
            fastcgi_pass unix:/var/run/php5-fpm-mainsite.sock;
            fastcgi_cache_bypass $no_cache;
            fastcgi_no_cache $no_cache;

            fastcgi_cache WORDPRESS;
            fastcgi_cache_valid 180m;
            }

            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 = /favicon.php { access_log off; log_not_found off; }
            location = /robots.txt { access_log off; log_not_found off; }
            location ~ /\. { deny all; access_log off; log_not_found off; }

            }

          • Avatar of Rahul Bansal

            Glad to know that your problem is solved. :-)

  15. Its better if access to the purge URL is limited to localhost only:

    location ~ /purge(/.*) {
    	allow 127.0.0.1;
    	deny all;
    	    fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
    	}	
    
    • Avatar of Rahul Bansal

      You can restrict it if you want but purging a cache won’t do any harm apart from wasting some CPU cycles.

      This unrestricted purge comes handy when a member of our team want to see his changes immediately. We have a big team working on different parts of this site. So whenever a dev working on http://rtcamp.com/store/ want to force cache-invalidation for store only, then can simply open http://rtcamp.com/purge/store/ in browser.

      • Give your team members SSH access and they can purge the cache using wget or curl.

        Anyway one reason why the purge should be restricted is that the page Nginx displays in response to a purge contains the full path of your cache. It’s better not to reveal this information to outsiders.

        • Avatar of Rahul Bansal

          Thanks for your suggestion but I still prefer /purge/ via browser. I have no energy to teach everyone SSH, etc specially for something so trivial as cache-purge.

          At the max, I will consider allowing direct-purges from our office-IP address. All machine’s gets same public-IP so that will be easy.

          About full-paths… Nginx cache dir is not web-accessibles so unless someone gets into server, they won’t be able to do anything with cache-dir directly. In this case, I will focus more on hardening server so that a hacker won’t be able to log into server in the first place!

          Please let me know if there is any serious known threat we can be subjected to just by exposing cache-dir location.

  16. Another point, keys_zone in fastcgi_cache_path specifies how much shared memory is going to be used to store meta data about the cache. The point being that it is memory not disk space. So setting it to 500MB or something high like that is asking for trouble. The example given on the nginx wiki is a much more realistic 10MB.

    If you want to restrict the maximum amount of disk space that the cache takes you have to use the max_size parameter.

    • Avatar of Rahul Bansal

      We have 32GB RAM! So 500MB is too less here. In fact, its 1.5625% of all available RAM. ;-)

      You are free to use whatever values you wish. :-)

      • That may be the case for you but a lot of people using Nginx are on a low end VPS and they are using Nginx primarily to save memory.

        • Avatar of Rahul Bansal

          That is the point! People can modify their config. In fact, they need to modify atleast domain name from example.com to their own domain.

          I will prefer to have values that we use on our client sites so that we can speed-up our client work. Most of our clients use more than 16GB RAM so 500MB cache is still very much practical.

          Another thing – rather than using VPS, you can go for dedicated servers from http://servercraft.co/dedicated-servers/. There are few more companies, which offer 4GB RAM for $49 and 8GB for $85. If you on Nginx, its better to go for such offerings IMHO.

  17. Thanks for great tutorials. Is it possible to setup Varnish on top of Nginx? I would love see you doing a post about it.

  18. Avatar of ovidiu

    Can you explain the reason why you don’t cache urls with a query string?
    Just being curios :-)

    POST requests and urls with a query string should always go to PHP

  19. What do you suggest when you are running new repos that provides nginx-common @ ver 1.2.7-0ubuntu0ppa1~precise vs ver = 1.2.4-1ppa4~precise that Brian’s ppa provides? I’d prefer to stick with prerolled tested debs rather than compiling at each ver update.

  20. Hello, ran into smalll problem :)

    W: Failed to fetch http://ppa.launchpad.net/brianmercer/nginx/ubuntu/dists/quantal/main/source/Sources 404 Not Found

    W: Failed to fetch http://ppa.launchpad.net/brianmercer/nginx/ubuntu/dists/quantal/main/binary-amd64/Packages 404 Not Found

    E: Some index files failed to download. They have been ignored, or old ones used instead.

    Is there alternatives?
    Thank you!

  21. Rahul, my ubuntu server version is quantal, can i use this ngx_cache_purge by FRICKLE with Nginx helper plugin ? How to can i install the nginx-custom on my server? My nginx version is 1.2.7.
    Thanks ;D

  22. hello Rahul
    My nginx version is 1.2.7 i follow your guidance above, but i can not install nginx-custom.
    if I see from https://launchpad.net/~brianmercer/+archive/nginx it states as follow:
    PPA description
    nginx_1.2.4 for 12.04 (precise).

    so, what can i do???

  23. My site was hosted in Virtual Private Server using Nginx in front of Apache (so it has Apache previously installed, NginX was used as reverse Proxy) along with SuPHP. I came here referred by WordPress Plugin NginX helper. Am I need that plugin or I can safely run my WordPress blog without it? I’m new to VPS so kindly suggest me with the plugin.
    Thanks

  24. You have a crucial typo: nginx -V 2>&1 | grep nginx-cache-purge -o

    No one will find the module if it’s mispelled.

    Should be: nginx -V 2>&1 | grep ngx-cache-purge -o

    • Avatar of Rahul Bansal

      @Jon

      That is not typo. Its correct on Ubuntu (atleast).

      When you run nginx -V

      You get following output:

      nginx version: nginx/1.2.4
      TLS SNI support enabled
      configure arguments: --prefix=/etc/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-log-path=/var/log/nginx/access.log --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --lock-path=/var/lock/nginx.lock --pid-path=/var/run/nginx.pid --with-debug --with-http_addition_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_realip_module --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module --with-http_xslt_module --with-ipv6 --with-sha1=/usr/include/openssl --with-md5=/usr/include/openssl --without-mail_pop3_module --without-mail_smtp_module --without-mail_imap_module --add-module=/build/buildd/nginx-1.2.4/debian/modules/nginx-auth-pam --add-module=/build/buildd/nginx-1.2.4/debian/modules/nginx-echo --add-module=/build/buildd/nginx-1.2.4/debian/modules/nginx-upstream-fair --add-module=/build/buildd/nginx-1.2.4/debian/modules/**nginx-cache-purge** --add-module=/build/buildd/nginx-1.2.4/debian/modules/nginx-upload-progress --add-module=/build/buildd/nginx-1.2.4/debian/modules/headers-more-nginx-module
      
  25. Hello.I used below configuration but it seems fastcgi_cache_bypass not work.

    fastcgi_cache_path /dev/shm/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;

    set $skip_cache 0;
    #set $cache_uri $request_uri;
    if ($request_method = POST) {
    #set $cache_uri 'null cache';
    set $skip_cache 1;
    }
    if ($query_string != "") {
    #set $cache_uri 'null cache';
    set $skip_cache 1;
    }
    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';
    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_logged_in|w3tc_logged_out|wptouch_switch_toggle") {
    #set $cache_uri 'null cache';
    set $skip_cache 1;
    }
    location ~ /purge(/.*) {
    fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
    }
    location / {
    try_files $uri $uri/ /index.php?$args;
    }

    location ~ .*\.(php|php5)?$
    {
    try_files $uri =404;
    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;
    fastcgi_cache WORDPRESS;
    fastcgi_cache_valid 60m;
    fastcgi_pass unix:/tmp/php-cgi.sock;
    fastcgi_index index.php;
    include fcgi.conf;
    }

    The problem is,if a visitor access a page[e.g.post or index] and generate this page cache,logged in user will unexpected get this page cache.Luckily the nginx isn’t generate page cache from logged in user.

    So it seems fastcgi_cache_bypass not work.But i checked this configuration and NginX-wiki number of times.Everything looks correct.

    Server Environment:

    NginX 1.2.8[complied --with-http_ssl_module --with-http_gzip_static_module --with-ipv6 --add-module=../ngx_cache_purge-master]

    PHP 5.3.23 FPM-FCGI

    CentOS 5.9

  26. Avatar of Rahul Bansal

    closed keepalive connection is something you no need to worry about.

    May be you can try using our free support forum here – https://rtcamp.com/support/forum/wordpress-nginx/

    We can try to debug it there in-depth.

  27. At an overall level, what would you recommend between fastcgi_cache vs page caching using W3 Total Cache? Does it depend on volume of traffic?

    Mine is not a MU set-up and I don’t have data on traffic as the project is not live yet.

  28. Hi Rahul ,

    It seems loop-page-home.php is not getting cached ….

  29. Add extra header for browser cache

    if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/page/|/feed|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
    	expires epoch;
    	add_header Cache-Control "no-cache, no-store, must-revalidate, max-age=0";
    	add_header pragma no-cache;
    	add_header Last-Modified "";
    	set $skip_cache 1;
    	}
    
    • Avatar of Rahul Bansal

      First, we are not suppose to cache output of backend php scripts. If we do so, user-2 may see dashboard view of user-1 and so on!

      Second, it you want to force caching use expires max.
      expires epoch is used to disable caching.

  30. Yes, i meant not to cache to browser side using expires epoch; and add_header Cache-Control “no-cache, no-store, must-revalidate, max-age=0″; and not to cache on nginx cache set $skip_cache 1;
    Actualy i’m not implemented nginx cache for my heavy traffic website because i’m using varnish to cached it. Nginx is too complicated to purge cache but varnish is more simple.

Leave a Comment

  • Facebook
  • GitHub
  • Twitter
  • LinkedIn
  • Google

Your email address will not be published.

*

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

{ 3 Trackbacks }

  1. wordpress在VPS上的优化配置 « 永远的wing - wing's forever, wing's my love. (Pingback)
  2. Nginx + WordPress + Fastcgi_Cache 自动清空缓存 | 俊彦博客 (Pingback)
  3. Nginx + WordPress + Fastcgi_Cache 自动清空缓存 - VPS间谍 (Pingback)