=== Ploy Connect ===
Contributors: ploy
Tags: reverse proxy, seo, aeo, landing pages, ploy
Requires at least: 5.8
Tested up to: 6.8
Requires PHP: 7.4
Stable tag: 1.2.4
License: GPLv2 or later

Serve Ploy-built pages on your own WordPress domain. Pick the paths, Ploy serves the pages — your theme, plugins, and the rest of your site stay untouched.

== Description ==

Ploy Connect reverse-proxies selected URL paths on your WordPress site to pages built and hosted by Ploy (ploy.ai). Visitors and search/answer engines see your domain; Ploy serves the content.

* Manifest-driven routing: Ploy controls which routes are served — publish a page in Ploy and it goes live on your domain automatically
* Signed sync webhook so updates propagate in seconds, plus hourly background refresh
* Stale-while-revalidate manifest caching: a Ploy API blip never drops your routes
* Local overrides: add your own routes, remap paths (`/resources/* => /blog/*`), or veto manifest routes with `!/path` — your exclusions always win
* Origin URLs rewritten to your domain (links, canonical, og:url, JSON-LD)
* Real visitor user-agents forwarded so AI crawlers and analytics are seen correctly
* Built-in response cache with per-route TTLs (`ttl=` overrides), a purge button, and editor bypass (`?ploy_nocache=1`)
* Origin 404s can fall back to WordPress
* Fails open: if the Ploy origin is unreachable, WordPress handles the request
* No cookies are forwarded to the origin; wp-admin, wp-json, wp-login and core paths can never be proxied

== Installation ==

1. Upload the `ploy-connect` folder to `/wp-content/plugins/`, or install the zip via Plugins → Add New → Upload.
2. Activate the plugin.
3. Go to Settings → Ploy Connect.
4. Paste your Ploy origin URL (Ploy dashboard → Site → Domains → WordPress integration).
5. Add the routes you want Ploy to serve, save, and click "Test connection".

== Frequently Asked Questions ==

= Will this conflict with existing WordPress pages? =
A proxied route shadows any WordPress content at the same URL. With "404 handling" enabled, WordPress takes over whenever Ploy has no page at that path.

= Does it work with full-page caching (WP Rocket, Varnish, host-level caches)? =
Yes — proxied responses include standard Cache-Control headers. Set the plugin's TTL to match your publishing cadence and purge both caches after big releases.

= Is POST/form submission proxied? =
Only to Ploy's own endpoints: POST is proxied solely for paths under `/_ploy/` (analytics ingest and Ploy form submissions), and only when the Ploy manifest publishes a matching `/_ploy/*` route. WordPress forms and every other POST are never proxied.

== Changelog ==

= 1.2.4 =
* Fix: stopped forwarding the CF-Connecting-IP header to the Ploy origin. When your WordPress site is behind Cloudflare and the Ploy origin is also behind Cloudflare, the forwarded CF-Connecting-IP caused the origin's Cloudflare to reject the request with Error 1000 ("DNS points to prohibited IP") as HTTP 403 — so every proxied page returned a 403. The real visitor IP is still forwarded to Ploy via X-Forwarded-For. (Regression from 1.2.0, which introduced CF-Connecting-IP forwarding.)
* The real visitor IP is now re-stamped to the Ploy origin as both X-Forwarded-For and True-Client-IP (a loop-safe replacement for the dropped CF-Connecting-IP), and is also detected from an incoming True-Client-IP header, so sites fronted by Cloudflare Enterprise or Akamai still pass Ploy the real client IP.

= 1.2.3 =
* Security: route matching now decodes the request path and compares reserved paths and your exclusions case-insensitively, so a percent-encoded or mixed-case URL can no longer slip past a `!`-exclusion (or a core wp-* path) and be served by Ploy

= 1.2.2 =
* Fix: targeted cache purges from Ploy now clear every cached variant of a page (regression in 1.2.1 left per-path purges as no-ops, so published edits could stay stale until the cache expired)
* Reliability: a circuit breaker now skips the origin and falls back to WordPress immediately when the Ploy origin is repeatedly failing, instead of each request waiting out the timeout
* Security: signed manifest-sync webhook requests are now single-use, rejecting replays within the validity window

= 1.2.1 =
* Security: the proxy response cache is now keyed by a coarse User-Agent class so a crawler-specific page is never served to human visitors (or vice versa), and responses carrying a Vary header are no longer shared across visitors
* Security: stopped forwarding the visitor Referer to the origin (prevents reflected cache poisoning); analytics referrer is still captured by Ploy's client-side beacon
* Security: cache keys now ignore tracking query parameters (utm_*, fbclid, gclid, …) and parameter order, so campaign links no longer multiply origin fetches or cache entries
* Security: origin response headers are now filtered through an allowlist, so unexpected headers (Refresh, Link, CSP, CORS, Clear-Site-Data, …) are not relayed onto your domain

= 1.2.0 =
* POST is now proxied for Ploy endpoints under /_ploy/ (analytics ingest, form submissions), when the manifest publishes a /_ploy/* route — all other POSTs still go to WordPress
* Request body and Content-Type are forwarded for proxied POSTs (1 MB cap, enforced before buffering)
* Analytics ingest responds 204 immediately and forwards the beacon after the visitor is released (fastcgi_finish_request on PHP-FPM; short-timeout fallback elsewhere); form submissions still block for the real status
* A fixed set of Cloudflare edge headers (CF-Connecting-IP, CF-IPCountry, CF-Ray) plus Referer are forwarded to the origin
* The Ploy origin URL must now use https:// (http allowed only for local dev hosts), so the site token is never sent in cleartext
* Per-route cache TTL in local overrides: append `ttl=<seconds>` to any route line (e.g. `/blog/* => /blog/* ttl=600`, or `ttl=0` to disable caching for that route)
* Clarified TTL precedence in the admin UI: per-route override > manifest TTL > default Cache TTL

= 1.1.0 =
* Manifest sync: routes are now controlled from Ploy ({origin}/.well-known/ploy-manifest.json, authenticated by site token)
* Signed REST webhook (POST /wp-json/ploy-connect/v1/sync) for instant refresh and cache purge on publish
* Per-route and manifest-level cache TTLs
* Local route additions and `!` exclusions that override the manifest
* Remote kill switch and minimum-plugin-version gating

= 1.0.0 =
* Initial release: route-based reverse proxy, URL rewriting, caching, admin tools.
