Skip to main content

WordPress Proxy

The WordPress plugin stores nothing about errors, leads or pageviews itself: everything it collects is forwarded to the Platform. What it adds is server-side enrichment a page script cannot produce — the logged-in user, the WooCommerce cart, an IP the server vouches for — and that enrichment decides which calls still pass through WordPress at all.

What goes through WordPress, and what does not

DataPathWhy
Errors, interactions, session recordings, time-on-pageBrowser → Platform directly (/sdk/v1/*)Nothing WordPress could add
Pageview of an anonymous visitorBrowser → Platform directlyThe page states which product it is, signed by the shop's server (below)
Pageview of a logged-in visitorBrowser → WordPress → PlatformThe user id is read from the WordPress cookie on the request, never printed into the page
Leads and formsBrowser → WordPress → PlatformThe live WooCommerce cart, WooCommerce's session id and the user id are attached in PHP
SurveysBrowser → WordPress → PlatformKept same-origin so a strict Content-Security-Policy needs no exception
Orders, PHP errors, catalogue changesWordPress → Platform (server-side hooks)These originate in PHP; there is no browser request

The WordPress routes live under /wp-json/storepilot/v1/ and each one is rate-limited and checked against the page's Origin. A route that still exists but that the browser no longer uses (the anonymous pageview) stays registered for pages cached before the plugin was updated and for a shop whose Platform was unreachable when the loader was last fetched.

The page vouches for itself

An anonymous product page prints a short signed statement into window.StorePilot:

window.StorePilot = {
// ...
page_facts: "<base64url claims>.<hmac-sha256>" // { v: 1, path: "/product/red-hat", p: "917", n: "Red hat", l: "https://…" }
};

It is signed with a key derived from the site's API key, which only the shop's server and the Platform hold, so the Platform can believe a value that arrived from a browser: what is believed is a claim the shop's server made, carried unchanged. The claim names the page's path, so it cannot be attached to a pageview of another page (a client-side navigation to the cart keeps the old token, and the Platform simply refuses it there). It carries no user id and no expiry — a product is a fact about the URL, so a copy cached for a week still vouches correctly, and a logged-in render prints no token at all.

Server-side enrichment (leads)

When a checkout or form payload passes through the leads route, PHP attaches:

  • wp_user_id — the logged-in user, from the request's own cookie
  • wc_session_id — WooCommerce's own id for the visit, which is what lets an order that never rendered our hidden field still find its lead
  • The live cart: each line's parent product id, name, quantity and line total, plus the cart total in both the shop's display format and as a raw figure
  • The visitor's IP from the socket (or from a proxy header only when the site has declared its proxy, or is behind Cloudflare's published ranges)

Security

  • Origin check — every collector route accepts only requests whose Origin or Referer is the site's own host
  • Rate limits — each route has its own per-IP ceiling, keyed on the socket address so a forged header cannot buy more
  • The API key is never in the page — PHP adds X-StorePilot-Key when forwarding; the browser only ever sees the site's public key, which identifies the site and authorises nothing but collection from the site's own origin
  • Consent — the collector's consent flag is forwarded on every proxied route, so a site that requires consent in the dashboard is honoured whichever path a request took

Failures

A proxied request that cannot reach the Platform answers the browser with a named reason (unreachable, rejected_key, refused) rather than a generic error, is bounded by a short timeout on every route whose next call is its own retry, and trips a circuit breaker after three transport failures in ten minutes so a Platform outage cannot slow the shop down. Nothing is queued on the shop: the next pageview, heartbeat or cart change is the retry.