Lua/OpenResty implementation of NGINX fastcgi_cache_purge behavior.
This project provides a Lua handler that deletes the same on-disk FastCGI cache file that NGINX would purge with the commercial fastcgi_cache_purge directive. It is intended for OpenResty or nginx with ngx_http_lua_module.
It can also forward purge requests to peer cache nodes, which is useful when the same site is served by multiple OpenResty/nginx servers with local FastCGI caches.
- PURGE method support, for example
PURGE /some/page. - Optional WordPress nginx-helper compatible
GET /purge/...support. - Optional
GET /clearcacheendpoint to clear the whole FastCGI cache directory. - Uses
resty.purgefor local FastCGI cache purge. - Reports the computed NGINX cache file path for troubleshooting.
- Cluster forwarding with loop prevention via
X-Purge-Forwarded: 1. - Works with OpenResty,
lua-resty-purge, andlua-resty-http.
Install OpenResty, or nginx built with:
ngx_http_lua_modulelua-resty-purge, which providesresty.purgelua-resty-http, which providesresty.httponly when cluster forwarding to another node is used
Install the OpenResty Lua libraries with OPM:
opm get kwanhur/lua-resty-purge
# Required only if purge_cluster_ips contains remote nodes:
opm get ledgetech/lua-resty-httpClone this repository onto the OpenResty/nginx server:
git clone https://github.com/rwahyudi/nginx-fastcgi_cache_purge-lua.git /opt/nginx-fastcgi_cache_purge-luaThe main handler is:
/opt/nginx-fastcgi_cache_purge-lua/lua/fastcgi_cache_purge.lua
Make sure the OpenResty worker user can read the Lua file and can delete files from the FastCGI cache directory.
The important part is that resty.purge must receive the same cache path, cache levels, and cache key as the normal FastCGI cache.
Example:
http {
lua_package_path "/opt/nginx-fastcgi_cache_purge-lua/lua/?.lua;;";
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=PHP:100m inactive=60m max_size=10g;
server {
listen 443 ssl http2;
server_name example.com;
root /var/www/html;
index index.php index.html;
set $purge_cache_path "/var/cache/nginx/fastcgi";
set $purge_cache_levels "1:2";
# Use GET here so PURGE /page deletes the cached GET /page object.
set $fastcgi_cache_key_value "$scheme:GET:$host$request_uri";
set $purge_cache_key "$fastcgi_cache_key_value";
set $purge_cluster_ips "";
set $purge_cluster_scheme "https";
set $purge_cluster_ssl_verify 0;
set $purge_cluster_timeout 2000;
set $purge_allow_get 0;
set $clearcache_all 0;
location / {
if ($request_method = PURGE) {
rewrite ^ /__fastcgi_cache_purge last;
}
try_files $uri $uri/ /index.php?$args;
}
location = /__fastcgi_cache_purge {
internal;
allow 127.0.0.1;
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
content_by_lua_file /opt/nginx-fastcgi_cache_purge-lua/lua/fastcgi_cache_purge.lua;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_cache PHP;
fastcgi_cache_key $fastcgi_cache_key_value;
fastcgi_cache_methods GET HEAD;
fastcgi_cache_valid 200 301 302 60m;
fastcgi_cache_valid 404 1m;
fastcgi_cache_lock on;
add_header X-FastCGI-Cache $upstream_cache_status always;
}
}
}Reload OpenResty after changing the config:
openresty -t
systemctl reload openrestyor, on nginx-based installs:
nginx -t
systemctl reload nginxSend a PURGE request for the URL to remove from cache:
curl -i -X PURGE https://example.com/some/pageA successful purge returns a text response like:
PURGED
cache_key: https:GET:example.com/some/page
cache_hash: <md5>
cache_file: /var/cache/nginx/fastcgi/.../<md5>
server_addr: 10.0.0.11
Possible status codes:
200- cache file was deleted.404- cache file was not present.405- method is not allowed for this location.500- cache key/path configuration error or delete failure.
Some WordPress setups use the rtCamp nginx-helper plugin in GET purge mode. Add these locations if you want URLs like /purge/some/page to purge /some/page:
location = /purge {
set $purge_allow_get 1;
set $purge_original_uri "/";
set $purge_cache_key "$scheme:GET:$host$purge_original_uri$is_args$args";
allow 127.0.0.1;
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
content_by_lua_file /opt/nginx-fastcgi_cache_purge-lua/lua/fastcgi_cache_purge.lua;
}
location ~ ^/purge(/.*)$ {
set $purge_allow_get 1;
set $purge_original_uri $1;
set $purge_cache_key "$scheme:GET:$host$purge_original_uri$is_args$args";
allow 127.0.0.1;
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
content_by_lua_file /opt/nginx-fastcgi_cache_purge-lua/lua/fastcgi_cache_purge.lua;
}Then purge with:
curl -i https://example.com/purge/some/page
curl -i 'https://example.com/purge/some/page?preview=true'Add a private /clearcache endpoint if you want to remove every file below purge_cache_path:
location = /clearcache {
set $purge_allow_get 1;
set $clearcache_all 1;
allow 127.0.0.1;
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
content_by_lua_file /opt/nginx-fastcgi_cache_purge-lua/lua/fastcgi_cache_purge.lua;
}Run:
curl -i https://example.com/clearcacheThe handler refuses to clear an empty path or /.
If purge_cluster_ips is empty, or only contains the current node's IP, no remote forwarding happens and lua-resty-http is not loaded.
If purge_cluster_ips contains at least one remote node, install lua-resty-http; otherwise the local purge still runs but the response reports that cluster forwarding was skipped because resty.http is missing.
On every cache node, set the same list of peer nodes:
set $purge_cluster_ips "10.0.0.11 10.0.0.12";
set $purge_cluster_scheme "https";
set $purge_cluster_ssl_verify 0;
set $purge_cluster_timeout 2000;When one node receives a purge request, it deletes the local cache file and forwards the same request to every other node in purge_cluster_ips.
The handler compares each node with nginx $server_addr and skips the local node. It adds this header to forwarded requests:
X-Purge-Forwarded: 1
That prevents forwarding loops.
purge_cluster_ips accepts IPs or IP:port entries:
set $purge_cluster_ips "10.0.0.11:443 10.0.0.12:443";If you forward to private IP addresses while preserving the public Host header, TLS hostname verification may fail. In that case keep:
set $purge_cluster_ssl_verify 0;Set it to 1 only when the peer address and certificate validate correctly.
Do not expose purge endpoints publicly.
At minimum, restrict access with allow/deny, firewall rules, or both. PURGE and clear-cache endpoints can remove cached content for the whole site.
Recommended restrictions:
allow 127.0.0.1;
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;See nginx.conf.example for a complete OpenResty configuration.
MIT