=== Nivoli Edge ===
Contributors: calimonk
Tags: cloudflare, image-optimization, cache, webp, performance
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.48.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Right-sized WebP/AVIF images via Cloudflare Image Resizing, plus full-page HTML caching with tag-based purge: your whole site, from the edge.

== Description ==

Nivoli Edge serves both halves of your site from the edge: no theme changes, no asset migration.

**Images:** URLs rewrite through Cloudflare's Image Resizing service, transforming on the fly into right-sized, format-negotiated variants (WebP / AVIF where supported) without uploading or storing duplicate copies.

**Pages:** the built-in Page cache module emits Surrogate-Key / Cache-Tag headers on every cacheable page and fires surgical tag-based purges when content changes: only the pages featuring the changed post refresh, never the whole cache. Works with Nivoli, Fastly, Cloudflare Enterprise, or your own webhook.

One plugin, one API key: images and pages both served from the edge.

= Free (everything that runs on your own infrastructure) =

* **Images:** URL rewriting through your own Cloudflare zone: right-sized AVIF/WebP via native WP filters (`the_post_thumbnail`, srcset, Gutenberg, WooCommerce), the_content + full-page scan, catch-all optimization.
* **Rules:** presets + size-name / filename-glob bindings, one-click Size mapping from your theme's registered sizes.
* **Page caching:** tag headers + surgical tag purge on save/delete/comment, dispatched to Fastly, Cloudflare Enterprise, or your own webhook. Activity log + trace mode.
* **Audit & automation:** coverage audit with weekly regression email, runtime-misses log, fake-image detection & repair, pre-warm on save and after purges, purge-failure alerts, weekly header self-test, printable client report.
* **Tools:** `?cfdebug=1` overlay, live pipeline probe, Tinify source compression (your own API key), WP-CLI (status / activate / purge / probe / audit).

= The managed service (optional) =

Everything above is fully functional without an account. Connecting your Nivoli API key links the plugin to the managed edge, which adds what only a hosted service can do:

* **Managed image CDN:** we run the Cloudflare zone; no CF account, plan, or DNS work. Custom hostname (img.yoursite.com), CDN-level watermarking.
* **Managed page cache:** full-page HTML caching with no Cloudflare Enterprise plan required. Bundled in every plan; fair-use, doesn't touch your image quota.
* **Origin shield:** if your server goes down, the edge keeps serving the last good copy of every cached page (up to 7 days) and emails you when the shield engages and when your origin recovers.
* **Edge insights:** usage & quota, cache-hit / bandwidth / origin-offload stats, 48-hour hourly traffic chart, Top URLs (most-missed / most-requested), broken-image alerts and heaviest-images reports from edge analytics.
* **Edge controls:** per-path cache duration (TTL), query-param manager, cache protection (purge-IP allowlist), and dynamic content (WooCommerce cart and checkout safety, plus how the edge treats the Cache-Control headers your site sends).
* **Edge security:** one-click XML-RPC block, URL rules (block or redirect legacy paths at the edge), login country lock, and a hardened security-headers pack (HSTS, nosniff, frame and referrer policies), enforced before PHP or your security plugin runs (complements Wordfence/Sucuri, doesn't replace them).
* **Agency:** multi-site fleet view, one API key across sites, priority support.

= Requirements =

* **Free:** a Cloudflare zone with **Image Resizing** enabled for the image half (Pro+ plan or per-1000 pricing); a tag-aware edge (Fastly / CF Enterprise / your webhook) for the page-cache half. If Image Resizing isn't enabled the rewritten URLs 404; the Tools tab has a one-click probe to verify.
* **Managed:** none of the above; the managed edge provides both. Just an API key from your Nivoli account.

== Installation ==

1. Upload the plugin and activate it (or paste your API key on the **Managed Edge** tab; the managed edge provisions itself and fills the settings in for you).
2. Free / bring-your-own-zone: open **Nivoli Edge → Settings**, confirm the auto-detected image host + path prefix, toggle Enabled.
3. Add rules under **Image rules** if specific sizes need specific treatment, or let **Size mapping** create them from your theme's registered sizes in one click. Catch-all handles the rest with zero config.
4. For HTML caching, open **Settings → Page cache** and pick a backend (managed Nivoli with your API key, or your own Fastly / CF Enterprise / webhook).
5. The **Dashboard** shows whether everything's working and what the edge is doing for you.

== Frequently Asked Questions ==

= Does this require Cloudflare Pro? =

Cloudflare Image Resizing is bundled with Pro plans or available as per-1000-transforms pay-as-you-go. Either is fine.

= Will this break my theme? =

No. The plugin only modifies URLs at the filter boundary; the HTML structure your theme outputs is unchanged. Use the `no-cf` CSS class on any element to opt out.

= How is this different from Smush / ShortPixel / Optimole? =

Those plugins compress and re-host images on their own CDN. Nivoli Edge transforms on the fly from your origin: no asset duplication, no migration step, no storage bill.

= What's the difference between Free and Managed? =

Everything the plugin does on your own server is free and fully functional: image rewriting, page-cache purging, audits, prewarming, alerts, reports. Nothing phones home. An API key connects the plugin to the Nivoli managed edge: we run the Cloudflare zone and page cache for you (no Cloudflare setup at all) and the service adds what a plugin alone can't, such as edge-side usage statistics, per-URL traffic insights, edge URL rules and security shields, custom hostnames, watermarking, and multi-site fleet management.

= Does the page caching conflict with my security plugin (Wordfence, Sucuri)? =

No. Different layers: security plugins inspect requests inside WordPress/PHP; Nivoli Edge's ten shields (XML-RPC block, login country lock, login, comment and search flood limits, stray-PHP and WordPress surface locks, AI-crawler block, wp-admin IP lock, security headers) run at Cloudflare's edge, before the request reaches your server. It sheds junk traffic so your origin and your security plugin only see real visitors. They complement each other.

= Do I need to change my nginx / web-server config? =

Only if your origin runs its own micro-cache (nginx fastcgi/proxy cache, Varnish) and you use the manual purge trigger; the Cache protection pane shows the exact one-line snippet. A standard PHP-FPM origin needs no server changes at all.

== External services ==

The free tier's core image rewriting sends **no data to any external service**; it only rewrites `<img>` URLs in your site's HTML so browsers fetch through your own Cloudflare zone. Beyond that, the plugin contacts external services only for the specific, opt-in features listed below.

**Nivoli managed edge** (api at html-caching-admin.nivoli.workers.dev, dashboard at console.nivoli.com): used **only if you enter an API key**. On activation and on a daily background re-check it sends your API key, this site's URL, the plugin version, and the list of broken-image file paths you have marked handled (so the monthly report can exclude them; these are addresses the CDN already sees in its own traffic) to validate the key and provision your managed CDN/page-cache tenant; it then reads back the aggregate usage statistics shown on the dashboard. If you configure monthly reports or alerts, the recipient email address and optional report branding (a name and logo URL) are stored with your account. No visitor data is ever sent. Terms: https://nivoli.com/terms · Privacy: https://nivoli.com/privacy

**Cloudflare** (api.cloudflare.com): used **only if you configure the Cloudflare Enterprise page-cache backend** with your own API token, to dispatch tag-based cache purges when your content changes. Terms: https://www.cloudflare.com/terms/ · Privacy: https://www.cloudflare.com/privacypolicy/

**Fastly** (api.fastly.com): used **only if you configure the Fastly page-cache backend** with your own API token, to dispatch surrogate-key purges on content change. Terms: https://www.fastly.com/terms/ · Privacy: https://www.fastly.com/privacy/

**TinyPNG / Tinify** (api.tinify.com): used **only if you add your own Tinify API key and click "Shrink original"** on an image, to compress that source file. Only the image you choose is sent. Terms & Privacy: https://tinify.com/terms

== Screenshots ==

1. Dashboard: the whole-site hero and the Delivered / Protected / plan pillars at a glance.
2. Image rules: per-size rewrite presets, or one-click Size mapping from your theme.
3. The Managed Edge tab: image usage and quota, with the insights, shields and edge controls grouped down one rail.
4. Stats & overview: cache hit rate, 48-hour traffic chart, and blocked-junk breakdown.
5. URL rules: block or redirect legacy URLs at the edge, with per-rule fire counts and the "what was blocked" log.
6. Edge shields: XML-RPC block, login country lock, flood limits and the stray-PHP and WordPress surface locks, enforced before your origin.
7. Settings: image host, rewrite host, and a live probe that checks the pipeline end to end.
8. Dynamic content: WooCommerce carts and checkouts always bypass the cache, and you decide whether the Cache-Control headers your own site sends are measured or enforced.

== Changelog ==

Recent releases are listed below. The full history for every version is in
CHANGELOG.md, which ships with the plugin, and on the GitHub releases page.

= 1.48.2 =
Weekly header self-test: the plugin now tells the edge which address this
server connects from right before the probe, so the edge recognizes its own
site even when the server reaches it over IPv6 or from an address DNS does not
name. Fixes the false "Page-cache headers stopped emitting" alert on servers
with IPv6 whose origin hostname has no AAAA record. A probe that runs in the
minute after a new address is registered is recorded as pending, not alerted.

= 1.48.1 =
Edge shields pane: the one-switch cards sit in two rows, the three flood
limiters together and AI crawlers with security headers below, instead of five
narrow columns. The WordPress surface lock card has one Block control; the
duplicate "Turn on blocking" button is gone.

= 1.48.0 =
Three new edge shields, all Growth and up, all off until you turn them on.
WordPress surface lock: anonymous requests for the REST users list (also via
?rest_route=), the ?author=N enumeration trick, readme.html, license.txt, the
installer and debug.log get a cached 404 at the edge; logged-in sessions are
never affected; off, monitor or block, with the measured list while
monitoring. Comment flood limit: 5 posts to the comment form per 5 minutes
per address. Search flood limit: 30 searches per 5 minutes per address. Two
separate switches. The Attack surface strip gains a tile for each, and the
Dashboard's Protected pillar now reads Floods stopped (login, comment and
search) with the surface lock counted among the probes, the same sums the
monthly report uses.

= 1.47.1 =
Heaviest images: "Shrink original" refused files whose folder or file name
contains two dots in a row ("File not found under the uploads directory" on a
file that was serving fine). The traversal guard checked for ".." anywhere in
the path; it now checks path segments, the same rule the placeholder flow
already used, so real file names pass while "../" stays blocked.

= 1.47.0 =
Heaviest images: the "shaved off your source files" total was a sum over the
activity log, which keeps 50 entries, so it stopped moving once the log was
full while the Dashboard kept counting. It now reads the same durable counter
the Dashboard uses. The block also shows the breadth of the saving: bytes
shaved on disk, the estimated bandwidth saved per month from each file's
measured request rate, and the count of originals shrunk and restored.

= 1.46.1 =
Maintenance release: the 1.46.0 build carried no functional change here; this
re-cut exists because a local tool cache reached the release zip and the
build gate caught it. Nothing in the plugin changed.

= 1.46.0 =
The Dashboard for free / own-zone installs, redesigned to match the managed
view: a hero built from the coverage audit (share of content images
optimized, images rewritten to WebP/AVIF, runtime misses this week), three
pillars (Coverage, Image health, Page cache) from what the plugin measures on
your own server, and a one-line mode footer. No locked pro cards, no pitch
card; the Dashboard is a work surface.

= 1.45.3 =
WordPress.org review pass. The page-cache self-test no longer pins its request
to IPv4 through cURL (the managed edge now recognizes a site's own server by
its IPv6 address too), and the Cloudflare-Enterprise backend card drops its
console link. Plugin Check: 0 errors, 0 warnings.

= 1.45.2 =
After merging redirects into one pattern, the page now lands on the new rule:
the "not used" fold opens, the row is highlighted and says its usage counts
from now (a rule made a second ago has no hits yet), and the success message
actually renders.

= 1.45.1 =
A media-library URL cut short of its filename (no extension under
/wp-content/uploads/, a client that truncated the request at a space in a
legacy filename) no longer counts as a broken image anywhere: not on the
Dashboard, not in the Broken images pane, not in the monthly report. The
edge (admin-v2.17.3) filters the same shape.

= 1.45.0 =
The Redirects pane, rebuilt as a work surface. It opens with what the
redirects did (requests redirected, rules split into patterns and exact
URLs, how many went unused), proposes merging families of exact rules that
differ by one token into a single pattern rule (one click, with the rules
it replaces listed), sorts the list by usage and folds unused rules away.
The 404 inbox now fills: the edge (v2.5.18) records the origin's status on
cached serves, so a cached 404 counts every time it is requested instead
of once per refresh.

