An nginx module that adds cache purge support for FastCGI, proxy, SCGI,
and uWSGI caches. A purge operation removes the cached entry whose key
matches the purge request.
- Features
- Compatibility
- Installation
- Directives
- Partial key purge
- Sample configurations
- Performance tuning
- Monitoring and debugging
- Troubleshooting
- Testing
- Migration
- Security
- License
- Inline purge — dedicated HTTP method (
PURGE) with IP access control - Separate purge location — 3-arg
proxy_cache_purge zone keysyntax for regex-captured key purging without aproxy_pass - Wildcard / partial purge — trailing
*walks the cache directory and removes all matching entries purge_all— removes every entry in the cache zone in one request- Background queue — async purge processing with configurable batch size and throttling so purge I/O does not block worker event loops
- Vary-aware purge — after an exact-key purge, removes all filesystem variants (gzip, Vary header) sharing the same cache key
- Response types —
html(default),json,xml,text
| nginx | status |
|---|---|
| 1.20.x | ✓ tested |
| 1.26.x | ✓ tested |
| 1.28.x | ✓ tested |
| 1.29.x | ✓ tested |
Older releases back to 1.7.9 compile but are not covered by CI.
cd /path/to/nginx-source
./configure --add-module=/path/to/ngx_cache_purge
make
make installDynamic module:
./configure --add-dynamic-module=/path/to/ngx_cache_purge
make modulesSyntax: proxy_cache_purge on | off | <method> [purge_all] from all | <cidr> ...
proxy_cache_purge <zone> "<key_expression>"
Default: —
Context: http, server, location
Inline form (from …) — intercepts the named HTTP method on a proxy
location and purges the matching cache entry. on is a shorthand for method
PURGE; off disables purging. Optionally restrict to a list of CIDR ranges
or use from all to allow from any address. Adding purge_all before from
empties the entire cache zone regardless of the request URI.
Separate-location form (two arguments) — use inside a dedicated purge
location (typically a regex location capturing the cache key). Looks up the
cache zone by name and purges the compiled key expression. This form is
incompatible with proxy_cache and proxy_pass in the same location.
Syntax: fastcgi_cache_purge on | off | <method> [purge_all] from all | <cidr> ...
fastcgi_cache_purge <zone> "<key_expression>"
Default: —
Context: http, server, location
Equivalent to proxy_cache_purge but for FastCGI cache zones configured with
fastcgi_cache / fastcgi_cache_path.
Syntax: scgi_cache_purge on | off | <method> [purge_all] from all | <cidr> ...
scgi_cache_purge <zone> "<key_expression>"
Default: —
Context: http, server, location
Equivalent to proxy_cache_purge but for SCGI cache zones configured with
scgi_cache / scgi_cache_path.
Syntax: uwsgi_cache_purge on | off | <method> [purge_all] from all | <cidr> ...
uwsgi_cache_purge <zone> "<key_expression>"
Default: —
Context: http, server, location
Equivalent to proxy_cache_purge but for uWSGI cache zones configured with
uwsgi_cache / uwsgi_cache_path.
Syntax: cache_purge_response_type html | json | xml | text
Default: html
Context: server, location
Sets the Content-Type and body format of purge responses. Has no effect on
cache-miss responses (412 / 404), which are generated by nginx's built-in
error-page renderer.
Syntax: cache_purge_background_queue on | off
Default: off
Context: http
When enabled, wildcard and purge_all purge requests are enqueued and return
202 Accepted immediately; a per-worker background timer drains the queue in
batches. Has no effect on exact-key purges, which are always synchronous. When
disabled, all purges are processed synchronously in the request handler.
Syntax: cache_purge_queue_size <number>
Default: 1024
Context: http
Maximum number of entries the background queue can hold. Each slot occupies
roughly 1–2 KB of shared memory (2048 slots ≈ 3 MB). When the queue is full,
new wildcard / purge_all purge requests fall back to synchronous processing.
Only meaningful when cache_purge_background_queue on.
Syntax: cache_purge_batch_size <number>
Default: 10
Max: 64
Context: http
Number of queue entries processed per background timer tick. Values above 64
are clamped to 64 at startup (a warning is logged). Reduce this value if purge
operations cause iowait spikes; increase it for faster queue drain on fast
storage. Only meaningful when cache_purge_background_queue on.
Syntax: cache_purge_throttle_ms <milliseconds>
Default: 10
Context: http
Interval between background processing ticks. Also introduces a brief yield
every 100 files within a single directory walk to limit I/O pressure. Increase
on constrained or spinning-disk storage; decrease on NVMe. Only meaningful
when cache_purge_background_queue on.
Syntax: cache_purge_legacy_status on | off
Default: on
Context: http
Controls the HTTP status code returned when a purge request targets an entry that is not in the cache:
| value | status on miss |
|---|---|
on |
412 Precondition Failed |
off |
404 Not Found |
Default is on (412) for backwards compatibility with earlier releases.
Set to off to return 404 Not Found, the correct HTTP status for a
resource that does not exist (RFC 9110 §15.5.5).
Syntax: cache_purge_vary_aware on | off
Default: off
Context: http
When on, an exact-key purge walks the cache directory after deleting the
primary file and removes any remaining files that carry the same KEY: string.
This covers all Vary and gzip_vary variants of a cached response, which
are stored at different filesystem paths but share one logical key. The byte
immediately after the key string in each file is verified to be \n,
preventing false matches against keys that share only a common prefix.
Disabled by default because it adds a full cache directory walk per exact-key
purge. Enable it when gzip_vary on or Vary: headers cause multiple cache
files to be created for a single logical entry. Wildcard and purge_all
purges do not need this option — the walk they already perform catches every
variant regardless.
When the exact cache key is not known — for example because it includes cookie
values or query parameters — append * to the key to request a prefix match:
PURGE /images/header*
The * must be the last character of the URI. Ensure $uri appears at the
end of proxy_cache_key when using this feature, otherwise the prefix
match will not align with the stored key.
http {
proxy_cache_path /var/cache/nginx keys_zone=main:10m;
server {
location / {
proxy_pass http://backend;
proxy_cache main;
proxy_cache_key "$host$uri$is_args$args";
proxy_cache_purge PURGE from 127.0.0.1;
}
}
}curl -X PURGE http://localhost/images/logo.png
# 200 — entry purged
# 412 — entry not in cache (or 404 with cache_purge_legacy_status off)
# 403 — client IP not in the allowed listhttp {
proxy_cache_path /var/cache/nginx keys_zone=main:10m;
server {
location / {
proxy_pass http://backend;
proxy_cache main;
proxy_cache_key "$host$uri$is_args$args";
}
# Capture the cache key from the URI and pass it to the 3-arg form.
# No proxy_pass or proxy_cache required in this location.
location ~ ^/purge(/.*) {
allow 127.0.0.1;
deny all;
proxy_cache_purge main "$host$1$is_args$args";
}
}
}http {
proxy_cache_path /var/cache/nginx keys_zone=main:10m;
cache_purge_background_queue on;
cache_purge_queue_size 2048;
cache_purge_batch_size 20;
cache_purge_throttle_ms 10;
server {
location / {
proxy_pass http://backend;
proxy_cache main;
proxy_cache_key "$host$uri$is_args$args";
proxy_cache_purge PURGE from 127.0.0.1;
}
}
}http {
proxy_cache_path /var/cache/nginx keys_zone=main:10m;
server {
location /content {
proxy_pass http://backend;
proxy_cache main;
proxy_cache_key "$host$uri$is_args$args";
proxy_cache_purge PURGE purge_all from 127.0.0.1;
}
}
}curl -X PURGE http://localhost/anything
# 200 — entire cache zone clearedhttp {
proxy_cache_path /var/cache/nginx keys_zone=main:10m;
cache_purge_background_queue on;
server {
cache_purge_response_type json; # default for all locations in this server
location / {
proxy_pass http://backend;
proxy_cache main;
proxy_cache_key "$host$uri$is_args$args";
proxy_cache_purge PURGE from all;
}
location /admin/purge {
# Override to plain text for scripting
cache_purge_response_type text;
proxy_cache_purge main "$arg_key";
}
}
}http {
cache_purge_vary_aware on;
gzip_vary on;
}After purging a key, all gzip and Vary variants stored at different filesystem paths are also removed automatically.
The appropriate values depend on storage type, cached file count, and purge request rate. The table below gives reasonable starting points.
| Environment | queue_size |
batch_size |
throttle_ms |
|---|---|---|---|
| Small VPS — 1–2 cores, ≤ 2 GB RAM | 512 | 5 | 25 |
| Mid-range VDS — 4–8 cores, SSD | 2048 | 20 | 10 |
| Dedicated server — 16+ cores, NVMe | 8192 | 50 | 5 |
| High purge rate, any hardware | 8192 | 5 | 50 |
Queue memory: queue_size × ~1.5 KB. 2048 slots ≈ 3 MB of shared memory.
Throughput ceiling: batch_size ÷ throttle_ms × 1000 purges/sec. At
defaults: 10 ÷ 10 × 1000 = 1 000/s. On spinning disk or network storage,
keep batch_size low and throttle_ms high to avoid iowait spikes.
Successful background purges return 202 Accepted. The response body uses the
format set by cache_purge_response_type.
Relevant log messages:
| Level | Condition |
|---|---|
warn |
Queue full; item timed out in queue |
error |
Cache zone not found |
crit |
File deletion failed |
tail -f /var/log/nginx/error.log | grep "cache purge"Purge requests block worker processes
Enable cache_purge_background_queue on.
Queue full warnings in the log
Increase cache_purge_queue_size, or reduce the purge request rate.
High iowait during purges
Decrease cache_purge_batch_size and increase cache_purge_throttle_ms.
Queue drains too slowly
Increase cache_purge_batch_size and decrease cache_purge_throttle_ms.
412 responses where 404 is expected
Set cache_purge_legacy_status off.
Vary / gzip variants remain after purge
Enable cache_purge_vary_aware on.
The test suite uses Test::Nginx.
prove -r t/Individual suites:
prove t/basic.t
prove t/background_queue.t
prove t/config.t
prove t/memory.t
prove t/performance.tSee t/TESTING.md for Docker-based testing and the full
testing guide.
The module is backwards compatible. No configuration changes are required. To opt in to background processing, add:
http {
cache_purge_background_queue on;
}Replace the module. Background queue functionality is now built in. Map your
previous queue parameters to cache_purge_queue_size, cache_purge_batch_size,
and cache_purge_throttle_ms.
- Restrict purge endpoints with
allow/denydirectives or an upstream authentication layer. Never expose them publicly. - Set
cache_purge_queue_sizeto a value appropriate for your traffic to limit shared memory consumption under request floods. - Consider pairing purge locations with
limit_reqto rate-limit purge requests independently of normal traffic.
Copyright (c) 2009-2014, FRiCKLE <info@frickle.com>
Copyright (c) 2009-2014, Piotr Sikora <piotr.sikora@frickle.com>
Copyright (C) 2016-2026, Denis Denisov
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.