Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 

Repository files navigation

nginx-fastcgi_cache_purge-lua

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.

Features

  • PURGE method support, for example PURGE /some/page.
  • Optional WordPress nginx-helper compatible GET /purge/... support.
  • Optional GET /clearcache endpoint to clear the whole FastCGI cache directory.
  • Uses resty.purge for 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, and lua-resty-http.

Requirements

Install OpenResty, or nginx built with:

  • ngx_http_lua_module
  • lua-resty-purge, which provides resty.purge
  • lua-resty-http, which provides resty.http only 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-http

Install

Clone this repository onto the OpenResty/nginx server:

git clone https://github.com/rwahyudi/nginx-fastcgi_cache_purge-lua.git /opt/nginx-fastcgi_cache_purge-lua

The 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.

Basic OpenResty usage

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 openresty

or, on nginx-based installs:

nginx -t
systemctl reload nginx

Purging a single URL

Send a PURGE request for the URL to remove from cache:

curl -i -X PURGE https://example.com/some/page

A 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.

WordPress nginx-helper compatible GET purge

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'

Clearing the whole FastCGI cache

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/clearcache

The handler refuses to clear an empty path or /.

Cluster forwarding

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.

Security

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;

Full example

See nginx.conf.example for a complete OpenResty configuration.

License

MIT

About

Lua implementation of NGINX fastcgi_cache_purge behavior for FastCGI cache invalidation

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages