Skip to content

Where Clicks End, Commands Begin

Latest

Choose a tag to compare

@hsntgm hsntgm released this 08 Jun 19:03
4909980

🚀 Nginx Cache Purge Preload — v2.1.7

Cache More. Block Less.

🛠️ 71 files changed  |  ➕ 8,902 lines  |  ➖ 1,941 lines — Terminal Control. Wider Coverage. Smarter Preloading.


✨ What's New

🖥️ WP-CLI Integration (major milestone)

Full cache management now lives in the terminal — no browser required.

The wp npp command exposes 10 subcommands covering every core NPP operation. Destructive commands support --dry-run for safe inspection, and --porcelain on purge/preload for clean shell-script output.

Subcommand Description
wp npp purge Purge the entire cache, or a single URL via --page-url=<url>
wp npp preload Full-site crawl, single URL via --page-url=<url>, or --stop to kill a running job
wp npp status Runtime status summary — outputs table, json, yaml, or csv
wp npp log Tail the operation log (--lines=<n>) or truncate it (--clear)
wp npp settings get / set any plugin setting; --pretty adds a human-readable name column
wp npp settings-reset Reset a specific setting to its plugin default value
wp npp flush Clear all NPP transients — use after path changes or binary installs (rg, safexec, wget)
wp npp index-clear Clear the persistent URL→filepath index when entries go stale
wp npp schedule List all active NPP cron events
wp npp schedule-set Configure (--freq, --time) or cancel (--cancel) the scheduled preload

Common patterns:

# Purge a single URL — emit only the outcome token for scripting
wp npp purge --page-url=https://example.com/shop/ --porcelain

# Full-site preload — spawns wget crawl in the background, returns immediately
wp npp preload

# Kill a running preload without touching the cache
wp npp preload --stop

# Inspect all current settings as JSON
wp npp settings get --format=json

# Update a single setting
wp npp settings set nginx_cache_path /var/cache/nginx/fastcgi

# Reset the preload exclude-endpoints regex to the plugin default
wp npp settings-reset nginx_cache_reject_regex

# Schedule a daily preload at 03:00
wp npp schedule-set --freq=daily --time=03:00

# Clear the URL→filepath index after moving the cache directory
wp npp index-clear

# Flush transients after installing ripgrep, safexec, or wget
wp npp flush

Ideal for CI/CD pipelines, deployment hooks, post-migration cleanup scripts, and headless server management.


⚡ Smarter Preload Engine — Coverage Breakthrough (major milestone)

The core motivation for v2.1.7: cache every URL that is safe to cache — not blindly deny everything with a query string.

Prior versions excluded any URL containing a query string from preload, silently leaving entire classes of safe, cacheable URLs as permanent MISSes. The preload reject regex has been completely overhauled — from a blanket query-string exclusion to a surgical, named-parameter denylist that blocks only the handful of parameters that genuinely break caching.

Before After
Rule Any ?... → skipped Only known-bad params → skipped
Coverage Low — broad exclusion High — surgical exclusion
Result Invisible cache gaps HITs where there were MISSes

Sites upgrading from earlier versions will see a significant increase in preload scope and cache coverage — URLs with benign query strings that were previously skipped are now preloaded and cached.

⚠️ Preload behavior changes significantly after upgrading. Use the "Reset Default" button for "Exclude Endpoints" in Settings → Preload Options to activate the new defaults.

One more thing — check your Nginx config. Nearly every Nginx cache tutorial on the internet includes this rule:

if ($query_string != "") {
    set $skip_cache 1;
}

This blanket rule silently defeats the entire point of this release. If it's in your Nginx config, URLs with query strings will be preloaded by NPP but immediately bypassed by Nginx — they will never be served from cache. Replace it with surgical per-parameter conditions that match only the parameters that genuinely break caching (session tokens, cart IDs, etc.) and let everything else through. Your skip-cache logic and your preload denylist should mirror each other exactly. A compatible, production-tested Nginx configuration is available in the NPP Dockerized repository.

The same release also extends the default Cache Key Regex to support three additional $fastcgi_cache_key formats beyond the original:

$scheme://$host$request_uri
$host$request_uri
$host$uri$is_args$args

Use "Reset Default" on "Cache Key Regex" in Settings → Advanced to activate.


📡 Preload Feeds (new)

RSS and Atom feeds are now first-class citizens in the preload pipeline.

Previously excluded from preload by default, feed URLs can now be fully controlled at both site-wide and per-URL levels:

  • Main site feed (/feed/, /feed/atom/) — preloaded on demand or on schedule
  • Per-post comment feeds — preloaded alongside their parent post
  • Per-taxonomy RSS feeds — preloaded together with their archive pages

Toggle via the new "Preload Feeds" option in Settings → Preload Options. The expanded Purge Scope extensions (see below) ensure feeds are automatically invalidated on relevant content updates.


🎯 Advanced Tab — Preload All MISS (new)

A lighter, smarter alternative to Preload All — purpose-built for sites where the cache is already mostly warm.

Preload All Preload All MISS
First step Full cache purge No purge
Scope Every URL Only MISSes
Best for Cold cache / full refresh Cache already > 50% warm
Cost High Low

When cache coverage is already high, Preload All wastes resources: it wipes the cache, then re-crawls everything from zero. Preload All MISS skips the purge entirely and crawls only the URLs currently absent from cache — significantly lower I/O and CPU cost for large, mostly-warm caches.

Available directly in the Advanced Tab (Cache Manager). Activates only when coverage exceeds 50% to prevent accidental use on cold caches.


🗂️ Expanded Purge Scope (extended)

Purge Scope now reaches further than ever — every major URL type is covered automatically when content changes:

Event Newly Purged
Post save / update Author archives
Post save / update Date-based archives (year, month, day)
Post publish / update Main site RSS feed
Comment activity Per-post comment feeds
Taxonomy update Per-taxonomy RSS feeds + archive pages
Comment pagination enabled Paginated comment URLs (pretty + query-string variants)
Any taxonomy update All public registered taxonomies + WooCommerce product attribute archives

No configuration changes required — existing Purge Scope settings are extended automatically.


🧡 Walkie (fun)

Meet Walkie — a tiny animated character who lives in the plugin header, strolls along the ribbon, and delivers cache-related commentary during long preload runs.

He fully believes he is a real AI assistant. He is not. But he is excellent company when a full-site preload has 4,000 URLs left to go.

Purely cosmetic. Zero performance impact.


🐾 Additional Highlights

  • "Test Regex" button — live-verify your Cache Key Regex against a real cache file directly from the settings page, without guesswork.
  • ripgrep --no-mmap flag — added to all cache scans for faster I/O on large directories of small binary cache files.
  • Related Preload engine refactor — collapsed dozens of parallel background operations into a maximum of 2 (Desktop + Mobile), sharply reducing server load spikes on Purge Scope triggers.
  • ripgrep version enforcement — strict minimum of >= 14.0.0 now required. Outdated binaries are flagged in the Status tab and gracefully demoted to the PHP fallback on FP3.
  • open_basedir compatibility detection — admin warning now fires when required plugin paths are blocked by open_basedir restrictions.
  • aaPanel compatibility — fully tested and functional across both Single and Multi WebServer architectures. (Thanks to @neikoloves)

🔒 Performance & Reliability

  • Critical Fix: Advanced Tab single Purge action no longer freezes the browser on large caches (30,000+ rows) when Purge Scope sub-triggers are enabled. Replaced O(N) full-table scans with an O(1) URL→row index cache and batched DataTable redraws.
  • Critical Fix: False-negative in Nginx detection on Nginx+Apache proxy stacks that rendered the plugin completely non-functional even when Nginx was active as the front proxy.
  • Fixed unprotected shell_exec and exec calls across the REST API, WP Cron, Dashboard Widget, and all other relevant execution paths.
  • Fixed recurring cron events (nppp_index_updater_event, npp_cache_preload_event) silently dying after their first run.
  • Fixed nppp_index_updater_event self-healing — event now auto-reschedules on the next admin load if wiped externally by WP-CLI, a cron manager plugin, or a database import.
  • Fixed URL→Filepath index not auto-flushing on WordPress permalink structure changes.
  • Fixed URL→Filepath index not auto-flushing when the Nginx Cache Path setting changes.
  • Fixed settings and option updates (REST / WP-CLI / UI) being permitted during active purge or preload operations.
  • Fixed an edge case where users were locked out by the transient cache when the Status tab and "Clear Plugin Cache" button became inaccessible while plugin functionality was disabled.
  • Fixed broken Nginx cache keys listing in the Status tab.
  • Fixed redundant process-killing by separating safexec and native kill paths in premature process detection logic.
  • Fixed missing getenv / putenv check for URL Normalization (safexec).
  • Fixed default Cache Key regex embedding non-GET/HEAD HTTP methods into the host segment on $scheme$request_method$host$request_uri keys — resolving silent purge failures on affected configurations.
  • Resolved multiple PHP 8+ deprecation warnings for null and false values passed to trim() and end().
  • Lowered TTLs of several internal plugin transients — the UI now reflects configuration changes more quickly, reducing the need for manual "Clear Plugin Cache" after adjustments.

🗑️ Changed / Updated

  • Hard dependency extended: both shell_exec and exec are now required. ⚠️ REGRESSION — verify these are not blocked by disable_functions in your PHP configuration before upgrading.
  • ripgrep minimum version >= 14.0.0 is now strictly enforced. FP3 Purge degrades to PHP recursive fallback on detection of an older binary.
  • DataTables assets bumped to v2.3.8 for improved Advanced Tab rendering and stability.
  • nppp_purged_all action hook — fired after every successful full Nginx cache purge, enabling third-party plugins to trigger their own cache flush in sync.
  • Tested up to: WordPress 7.0

🧩 Where to Find Things

Feature Location
WP-CLI commands Terminal → wp help npp
Preload Feeds toggle Settings → Preload Options
Exclude Endpoints (Reset Default) Settings → Preload Options
Cache Key Regex (Reset Default + Test) Settings → Advanced
Preload All MISS Advanced Tab → Cache Manager
ripgrep version status Status Tab
open_basedir warning Admin notice (fires automatically if applicable)
Walkie Plugin header — always on

🔄 Upgrade Notes

  1. Update the plugin to v2.1.7 as usual.
  2. ⚠️ Use "Reset Default" on "Exclude Endpoints" in Settings → Preload Options — the preload reject regex has been completely overhauled. Preload behavior changes significantly. Verify your Nginx skip-cache rules are compatible with the expanded preload scope before going live.
  3. ⚠️ Use "Reset Default" on "Cache Key Regex" in Settings → Advanced to activate expanded cache key format support.
  4. Confirm ripgrep ≥ 14.0.0 is installed on your server. Check the Status tab — outdated binaries are flagged and degraded automatically.
  5. Confirm both shell_exec and exec are enabled in your PHP configuration — these are now hard dependencies. Sites with disable_functions restrictions blocking either function will lose cache management functionality.

🙏 Credits

Huge thanks to everyone who tested and reported issues. Special shout-out to @neikoloves for thorough aaPanel compatibility issues and detailed reporting across both WebServer architectures. 💙


📜 Changelog

  • Added (Milestone): Full WP-CLI integration — wp npp command with 10 subcommands: purge, preload, status, log, settings, settings-reset, flush, index-clear, schedule, and schedule-set. Supports --dry-run, --porcelain, and multiple output formats (json, yaml, csv) across subcommands.
  • Added: "Preload Feeds" — full control over site-wide and per-URL feed caching (main RSS/Atom, per-post comment feeds, taxonomy RSS feeds).
  • Added: Advanced Tab "Preload All MISS" — lightweight alternative to Preload All; skips purge step and only preloads currently-missing cache entries. Activates when cache coverage exceeds 50%.
  • Added: Walkie — animated plugin header character with cache-related commentary. Purely cosmetic.
  • Added: "Test Regex" button — live-verify Cache Key Regex against a real cache file from the settings page.
  • Added: Strict ripgrep minimum version requirement (>= 14.0.0).
  • Added: Cache Key Regex validation — global admin warning when custom or default regex fails validation.
  • Added: open_basedir compatibility detection with admin warning for missing required paths.
  • Added: Detection for Vary: Accept-Encoding double-cache issue (fully dismissable).
  • Added: nppp_purged_all developer action hook — fires after every successful full Nginx cache purge.
  • Improved (Requires Action): Preload engine reject regex overhauled from blanket query-string exclusion to selective named-parameter denylist — significant increase in preload scope and cache coverage. Use "Reset Default" on "Exclude Endpoints" to activate.
  • Improved (Requires Action): Default Cache Key Regex extended to support $scheme://$host$request_uri, $host$request_uri, and $host$uri$is_args$args formats. Use "Reset Default" on "Cache Key Regex" to activate.
  • Improved: Nginx detection and Setup (Assume Nginx Mode) — detection is now prioritized before all other environment checks with clear instructions.
  • Improved: Setup Page UI/UX.
  • Improved: aaPanel compatibility — fully tested across Single and Multi WebServer architectures. (Thanks to @neikoloves)
  • Improved: Status tab diagnostic reporting — explicitly flags outdated ripgrep binaries.
  • Improved: FP3 Purge degrades gracefully to PHP recursive fallback when an outdated ripgrep binary is detected.
  • Extended: Purge Scope now purges Author archives on post save/update.
  • Extended: Purge Scope now purges Date-based archives (year, month, day) on post save/update.
  • Extended: Purge Scope now purges RSS feeds on relevant events — main feed on post publish/update, per-post comment feeds on comment activity, per-taxonomy RSS feeds alongside archive pages.
  • Extended: Purge Scope now purges paginated comment URLs (pretty-permalink and query-string variants) when WordPress comment pagination is enabled.
  • Extended: Purge Scope taxonomy purging now covers all public registered taxonomies generically, including WooCommerce product attribute archives.
  • Performance (Critical Fix): Advanced Tab single Purge action no longer freezes the browser on 30,000+ row caches with Purge Scope sub-triggers enabled — replaced O(N) full-table scans with O(1) URL→row index cache and batched DataTable redraws.
  • Performance: Added --no-mmap flag to all ripgrep cache scans for faster I/O on large directories of small binary cache files.
  • Performance: Related Preload engine restructured into a single process — collapsed dozens of background operations into a maximum of 2 (Desktop + Mobile).
  • Fixed: False-negative Nginx detection on Nginx+Apache proxy stacks rendering the plugin non-functional.
  • Fixed: Unprotected shell_exec and exec calls across REST API, WP Cron, Dashboard Widget, and all relevant execution paths.
  • Fixed: Missing getenv / putenv check for URL Normalization (safexec).
  • Fixed: User lockout edge case — transient cache blocking access to Status tab and "Clear Plugin Cache" when plugin functionality is disabled.
  • Fixed: Broken Nginx cache keys listing in the Status tab.
  • Fixed: Settings/option updates now blocked during active purge/preload operations to ensure process state consistency.
  • Fixed: Redundant process-killing — separated safexec and native kill paths in premature process detection logic.
  • Fixed: Recurring cron events (nppp_index_updater_event, npp_cache_preload_event) silently dying after their first run.
  • Fixed: nppp_index_updater_event self-healing — auto-reschedules on next admin load if wiped externally.
  • Fixed: URL→Filepath index now auto-flushed on WordPress permalink structure changes.
  • Fixed: URL→Filepath index now auto-flushed when Nginx Cache Path changes.
  • Fixed: Default Cache Key regex no longer embeds non-GET/HEAD methods into the host segment on $scheme$request_method$host$request_uri keys.
  • Resolved: Multiple PHP 8+ deprecation warnings for null / false values passed to trim() and end().
  • Changed: Hard dependency extended to require both shell_exec and exec. ⚠️ REGRESSION — verify PHP configuration before upgrading.
  • Removed: False-positive and redundant warning messages from Status and Advanced tabs.
  • Balanced: Lowered TTLs of several internal plugin transients for faster UI reflection of configuration changes.
  • Updated: DataTables assets to v2.3.8.
  • Updated: Tested up to WordPress 7.0.
  • Compatibility: Tested with Nginx (1.31.1), FUSE (3.18.2), bindfs (1.18.4), safexec (1.9.6), ripgrep (15.1.0), wget (1.25.0), and aaPanel (8.0.3).