FRiCKLE/ngx_cache_purge

This module not working as expected

kigajewex opened this issue · 8 comments

I'm not saying wasting of time on it but it just not working as expected, so i build my own purge, kinda fast just in few minutes

proxy_cache_path /var/nginx_cache levels=1:2 keys_zone=my_cache:20m max_size=200m inactive=2h;
proxy_cache_key $uri;
server {
	server_name purge;
	root /var/www/purge;
	index target.php
	listen 80;
	location / {
		try_files $uri $uri/ target.php;
	}
	location ~ \.php$ {
		include snippets/fastcgi-php.conf;
		fastcgi_pass 127.0.0.1:9000;
	}
}
<?php
if(!isset($_POST['uri'])){
	die('no uri');
}
$uri = $_POST['uri'];
$md5 = md5($uri);
$parent = substr($md5, -1);
$sub = rtrim(substr($md5, -3), $parent);
if(@unlink('/var/nginx_cache/'.$parent.'/'.$sub.'/'.$md5)){
	echo 1;
}else{
	echo 0;
}

wow great work haha
i used your code as inspiration to create a new one on python to clean slice caches :)

Znuff commented

I'll piggy-back on this post, hopefully it catches the attention of more people and they can be saved the 2-3 hours of time I invested in trying to figure out this whole mess.

First of all, if you are a Debian or Ubuntu (or derivatives) user, know that YES this is the github repository of the module packaged as libnginx-mod-http-cache-purge in your distribution. This module is also in use if you use Ondřej Surý's PPA.

When I set up on the path of fastcgi_cache, and after I scoured the internet, I was obviously interested in a way to PURGE the cache of fastcgi.

Amongst the first few articles I found (and that you will probably find, too), are all mentioning about fasctcgi_cache_purge.

Sadly, as you dwelve deeper, you will find that fastcgi_cache_purge is only part of the subscription-model NGINX Plus, which I'm sure it's a great product, but I've learned the lesson that if you have to ask for what the price of a product is (they are not displaying it anywhere public), it's too expensive.

Luckily, you can see that there are quite a few 3rd party modules for nginx that mention cache purging and fastcgi.

This is one of them. Or, I should say, this was one of them.

I've attempted all the possible configuration variants to get this to work, but alas, to no avail.

So to save you some time: DO NOT USE THIS MODULE, IT DOES NOT WORK


Example of a configuration that does not work ("separate location syntax")

fastcgi_cache_path /var/cache/nginx/ levels=1:2 keys_zone=App:100m;
fastcgi_cache_key "$request_method$host$request_uri";

server {
  listen 80;
  add_header X-Cache $upstream_cache_status;
  
  # your other config variables
  
  location ~ .\php {
    # all your fastcgi params
    
    fastcgi_cache_valid 200 1 d;
    fastcgi_cache App;
  }
  
  location ~ /purge(/.*) {
    # $1 is the part of the URL after /purge, and we are using that instead
    # $request_uri would otherwise start with /purge and our KEYs would not match
    fastcgi_cache_purge App "$request_method$host$1"; 
    return 200 "PURGED: $request_method$host$1"; 
  }
}

Explanation: we are using $request_method$host$request_uri as a KEY for caching, because we want to cache HEAD and GET requests separately. If you do not use $request_method in your KEY, you will sometimes get empty results from the cache. This is because when someone does a HEAD /uri, nginx returns to body (because it was obviously asked to return only the HEADers), thus if someone does a HEAD before a GET versus the same URI, the empty HEAD will be cached.

So in order to trigger this, we should simply be able to append the $request_uri to /purge/ and it should clear, per fastcgi_cache_purge.

# first check for the cache status:

$ curl -I http://app/example.php
HTTP/2 200                                                        
server: nginx/1.16.1                                              
[...]
x-cache: HIT          

# then send the request to /purge/:

$ curl http://app/purge/example.php
PURGED: GETapp/example

This will do absolutely nothing.

Obviously, you can not use PURGE when you are using $request_method as a KEY, because the KEY will not match when purging.

Example 2, ("same-location syntax", no $request_method)

To use the PURGE method, you will have to alter your KEY to something like $host$request_uri, but that will lead to the problem I mentioned above, about caching and serving blank pages if someone does a HEAD on your URI.

Nevertheless, I have tested this, too.

Config like above, but:

fastcgi_cache_key "$host$request_uri";

server {
 # ...

  error_page 405 $request_uri; # to keep it from throwing a 405 method not allowed

  location ~ .\php {
  # same as above, just add:
  fastcgi_cache_purge PURGE from all;
  }
  # remove the /purge location block
}

This should be as easy as:

$ curl -X PURGE http://app/example.php

Yet... nothing happens. The cache stays on the disk, the same file is served.

Example 3, separate server{} block and "separate location"

This is the example from https://groups.drupal.org/node/125094

We remove any reference about fastcgi_cache_purge from our main server{} block, and we add another one that listens on a different port and it's only purpose is to serve as an endpoint for cache clearing

server {                                            
  listen 9898;                            
  error_page 405 $request_uri;                      
  location / {                                      
    fastcgi_cache_purge App "$host$request_uri";
    return 200 "Purged: $host$request_uri";                                     
  }                                                 
}                                                   

In theory, this should do fastcgi_cache_purge for our App's $host$request_uri, regardless of method we use to access this, so either one of these should work:

$ curl http://app:9898
Purged: app/

$ curl -X PURGE http://app:9898
Purged: app/

Yet, again, nothing actually happens. Cached file is still served.

You should not use "return" in the test I believe because it is a part of rewrite module and it most probably breaks expected behavior.

Znuff commented

I used return only for testing. Without it, you get a 405, and no, it still doesn't work - cache won't purge.

@Znuff

May I ask, is your stack configured for per site PHP users? In which case, yup, this module will not work, its been long and well documented.

Also, the solution at the top using PHP will only work if Nginx and the PHP application share the same user.

However many stacks use this, and it does work. Most notably EasyEngine, but like I say, single user stacks.

However, this module:
https://github.com/torden/ngx_cache_purge

Is a fork and works for multi-user stacks. How do I know? Because we have it compiled into our platform, and we have per application cache purging working fine with multitenancy LEMP stacks and each site in PHP user isolation.

First of all, if you are a Debian or Ubuntu (or derivatives) user, know that YES this is the github repository of the module packaged as libnginx-mod-http-cache-purge in your distribution. This module is also in use if you use Ondřej Surý's PPA.

For the LOVE OF GOD, Why was this information so difficult to find? I've been scouring the internet looking for an answer about both of these, and FINALLY I found it! Thank you so much @Znuff! You saved me a lot of wasted time and a lot more google searching with this information!

Sadly, as you dwelve deeper, you will find that fastcgi_cache_purge is only part of the subscription-model NGINX Plus, which I'm sure it's a great product, but I've learned the lesson that if you have to ask for what the price of a product is (they are not displaying it anywhere public), it's too expensive.

Luckily, you can see that there are quite a few 3rd party modules for nginx that mention cache purging and fastcgi.

This is one of them. Or, I should say, this was one of them.

I've attempted all the possible configuration variants to get this to work, but alas, to no avail.

So to save you some time: DO NOT USE THIS MODULE, IT DOES NOT WORK

I followed THIS TUTORIAL from Linuxbabe.com and got this module working properly after building it from source for my version of Nginx mainline, specifically Nginx v1.21.4 on a Raspberry Pi 4.

EDIT: I actually thought this module WAS working, but after further inspection, it was not actually purging the cache as expected. This module might as well be dead.