Technical reference

NORG Edge Router for WordPress

Plugin 0.1.3. The install steps are on the plugin guide; the architecture it sits in is on the solution architecture page; the one rule WP Engine needs is on its own page; the latency it adds, measured on Norg’s own test site, is on the performance results page. Everything here is read from the plugin source for this version, and the source is available on request before you install it.

Request lifecycle

The plugin hooks plugins_loaded at priority −1000, before any other plugin, and wraps its whole run in a catch: an exception is logged and the page is served normally.

Requests it never touches
  • Any request while disabled, or carrying the x-norg-edge loop-guard header or an upgrade header.
  • Admin, AJAX, cron, WP-CLI and XML-RPC requests; WordPress internal paths and rest_route requests.
  • Anything but GET, except HEAD on NORG’s own artefacts and POST to the MCP path.
Order of decisions
  1. A marked request (?norg_edge) is made uncacheable, whatever follows.
  2. Traditional search crawlers (googlebot, bingbot, applebot, duckduckbot, baiduspider, yandex, slurp) are passed through. This list is a constant; no setting reaches it.
  3. The user agent is matched against the policy feed by lower-cased substring; the first match wins.
  4. The matched pattern’s serving policy decides what happens, as below.

Classification and serving

The plugin ships no agent list of its own. Patterns, their serving policy and the operators’ address ranges all arrive in the policy feed; with no feed there is no behaviour.

Serving policies
Feed policyWhat the plugin does
never_divertServe the normal page. Scripts and HTTP libraries (headless browsers, curl, Python clients) are always in this class.
divertCheck the source address against the operator’s published ranges; serve the mirror only if it matches. The check is address-range only and fails closed.
divert_unverifiedServe the mirror without the address check, for an operator that publishes no ranges. Only ever returns an existing mirror; never triggers a render.
ignoreNeither divert nor count as crawler traffic (opt-out signals such as Google-Extended).
Address verification

Ranges are published by the operators and refreshed by NORG daily (OpenAI’s and Anthropic’s JSON files, Perplexity’s two files, Meta via its autonomous system). Which address is checked depends on NORG_TRUSTED_PROXY: auto takes CF-Connecting-IP from a Cloudflare peer, the last X-Forwarded-For or X-Real-IP hop from a private or shared-space proxy, else REMOTE_ADDR. A query string ?agent=true forces the mirror for a preview, if one exists.

Fallbacks

A 200 from the content service is served. A definite 404 triggers a render request and the strip fallback: the live page is captured in an outer output buffer, scripts, styles, navigation and other chrome are removed, and the result is served with X-Norg-Edge: stripped if at least 120 words remain; otherwise the untouched page is served. A transport error, timeout or any other status serves the untouched page.

Headers and page caches

Every mirror or stripped response is marked uncacheable in each dialect a cache might read, and the plugin defines the shared constants cache plugins honour.

Response headers
HeaderOn a mirror or stripped response
Cache-Controlprivate, no-store
CDN-Cache-Controlprivate, no-store
X-LiteSpeed-Cache-Controlno-cache
X-Accel-Expires0
X-Norg-Edgemirror, or stripped
X-Norg-Edge-Envproduction or test
Set-Cookie, Expires, Pragma, Last-Modified, Link, X-PingbackRemoved.
Cache plugins and hosts

On a mirror response the plugin defines DONOTCACHEPAGE, DONOTCACHEOBJECT, DONOTMINIFY and DONOTROCKETOPTIMIZE, and calls LiteSpeed’s no-cache hook. For agents to reach it in the first place, it registers the agent user agents with WP Rocket, LiteSpeed Cache, WP Super Cache, WP Fastest Cache, W3 Total Cache and FlyingPress when it detects them (the page caches section lists each mechanism). Caches that run before PHP at the host need a host rule; WP Engine’s is one internal rewrite. Mirror HTML is deliberately not marked noindex. NORG’s shared assets under /.norg/ are served cacheable for 60 s.

Settings and wp-config constants

Settings live in one option, norg_edge_settings, which is not autoloaded. A constant defined in wp-config.php always wins over the saved value and locks that field on the settings screen, which is how a host or agency pins a site’s configuration.

Constants and defaults
wp-config constantDefaultPurpose
NORG_SITE_ID(none)The site’s UUID in NORG. Required.
NORG_SITE_KEY(none)The site key (nek_live_… or nek_test_…). Defined here, it is never written to the database.
NORG_API_URLhttps://content-craft-api.norg.aiControl-plane host.
NORG_CONTENT_BASEhttps://edge-content.norg.aiContent-service host the mirrors are fetched from.
NORG_EDGE_ENVderived from the API hostproduction or test; echoed in the X-Norg-Edge-Env response header.
NORG_EDGE_DISABLEDfalseKill switch. Every visitor gets the normal site, effective on the next request.
NORG_CAPTURE_ONLYfalseRecord-only mode: report agent visits (and citation page-views), serve nothing different.
NORG_STRIP_FALLBACK_ENABLEDtrueWhen no mirror exists, serve the live page stripped to text (120-word floor).
NORG_LAZY_RENDER_ENABLEDtrueAsk NORG to render a page the first time a verified agent asks for it.
NORG_TRUSTED_PROXYautoWhere the visitor address is read from: auto, none, cloudflare, or header.
NORG_TRUSTED_PROXY_HEADER(none)The header to read when NORG_TRUSTED_PROXY is header.
NORG_EDGE_ORIGIN_PROBEtrueAnswer NORG’s keyed health probe (x-norg-edge-check).
NORG_EDGE_CACHE_BYPASStrueRegister the agent user agents with detected cache plugins.
NORG_EDGE_HTACCESS_BLOCKfalseWrite a generic .htaccess bypass block for Apache/LiteSpeed caches with no API.
NORG_EDGE_AUTO_UPDATEtrueInstall plugin updates from NORG automatically. false means each update is approved by hand.
NORG_EDGE_DEBUGfalseWrite a debug log to the PHP error log. The site key is never logged.

Where the Site Key lives

The Site ID must be a UUID and the key must match nek_live_… or nek_test_…. NORG stores only the key’s SHA-256 hash, so it cannot be recovered from NORG; what follows is how the plugin holds your copy.

Defined in wp-config.php (recommended)

The key is read from the constant on each request and never written to the database. The settings screen shows the field as locked, and the REST connect endpoint answers 409 rather than overwrite it.

Entered on the settings screen

The key is encrypted before it is saved, with a key derived from the site’s own authentication salt (wp_salt('auth')), using libsodium’s secretbox where available, otherwise AES-256-GCM via OpenSSL. Rotating the salts makes the stored key unreadable and it must be re-entered.

If neither sodium nor OpenSSL is available

The key is stored base64-encoded and unencrypted, and the status page says so (secrets scheme: plain). On such a host, define NORG_SITE_KEY in wp-config.php instead.

Outbound calls and budgets

All calls use the WordPress HTTP API with TLS verification on and redirects off, and identify themselves as norg-edge-router-wp/{version}. The control headers on every call are X-Norg-Site-Id, X-Norg-Site-Key and X-Norg-Edge-Version. {api} is content-craft-api.norg.ai and {content} is edge-content.norg.ai unless overridden.

Outbound calls
CallEndpointWhenBudgetSends
Policy feedGET {api}/api/v1/edge/bot-patternsWhen the cached feed is older than its cache_ttl (3,600 s default); refreshed on the next request, stale copy served meanwhile; kept up to 24 h while NORG is unreachable. A 401/403 switches serving off at once.5 sControl headers, If-None-Match
HeartbeatPOST {api}/api/v1/edge/heartbeatWP-Cron every 30 min; on traffic at most every 900 s; on connect and from the admin page.3 sPlugin version, trigger, environment, platform, dropped-event count, runtime block (WordPress and PHP version, SAPI, server, cache plugins, host, bypass result, secrets scheme, multisite).
Mirror fetchGET {content}/{site_id}{path}/index.htmlPer verified agent request.4 s, 8 MB capControl headers, x-norg-edge: 1, X-Norg-Visit (base64url JSON, 4 KB max: agent user agent, classification, outcome, HTTP protocol, country when behind Cloudflare).
Render requestPOST {api}/api/v1/edge/render-requestsOnly on a definite 404 from the content service; de-duplicated per path for 600 s.3 spath, url, reason: bot_miss
Update manifestGET {api}/api/v1/edge/wordpress/update-manifestCached 6 h; forced when a heartbeat answers update_available.5 s (package download 120 s)Control headers
MCP relayPOST {api}/public-mcp/When an MCP request addresses the site.10 sAllow-listed headers, control headers, X-Norg-Edge-Page
Visit events (record-only)POST {api}/api/v1/edge/eventsAfter the response, in record-only mode.3 shost, path, user agent, classification, status, verified flag, country, protocol
Citation page-views (record-only)POST {api}/api/v1/edge/page-viewsA person arriving from an AI answer or a search result, in record-only mode, while citation_tracking_enabled is on in the feed.3 sPseudonymous visitor id (truncated SHA-256 of address, user agent, Accept-Language and UTC date), user agent, referrer, utm_source/medium/campaign, country, device type, citation fields
What is never sent

Page content from WordPress. Cookies or authorisation headers. Anything at all for an ordinary human visitor while the plugin is in serving mode. The one flow that describes people, citation page-views, runs only in record-only mode and is described in the table above exactly as it is built.

Admin, REST, CLI and health surfaces

Every surface that reads or changes the plugin’s state, and who it is open to.

  • Settings screen. Settings → NORG Edge Router: Site ID and Site Key (masked; leave empty to keep), environment, visitor-address source, the switches above, status rows, the cache-plugin table, host advice, and the read-only list of agent user agents for your host.
  • REST. GET norg-edge/v1/status and POST norg-edge/v1/connect, both requiring manage_options. NORG’s dashboard uses them with a WordPress Application Password you issue and can revoke. Connect never returns the key and answers 409 when the values are pinned in wp-config.php.
  • WP-CLI. wp norg-edge configure, status, check, heartbeat and apply-cache-bypass, running the same code paths as the screen.
  • Site Health. Three tests under Tools → Site Health: connection, AI agents reach the plugin, background tasks.
  • Keyed health probe. A request carrying x-norg-edge-check equal to the Site Key (compared in constant time) receives a JSON status; off with NORG_EDGE_ORIGIN_PROBE.

Run checks

Run checks makes four loopback requests from the server to the site’s own home page, each with a 10 s budget. A loopback that cannot connect reports unverifiable rather than guessing.

Run checks
Run checks rowWhat it proves
Browser untouchedA browser user agent to the home page must come back without any X-Norg-Edge header.
AI agents reach pluginA GPTBot user agent carrying the health-check key must reach PHP: reached, or cache_interference when a cache answered instead.
Search engine untouchedGooglebot with the simulate header must come back untouched: rule two, proven on your own site.
Agent divertedGPTBot with the simulate header should return mirror, or stripped if no mirror exists yet.

Updates

Updates come from NORG, not wordpress.org. The plugin hooks WordPress’s own update check for its Update URI on content-craft-api.norg.ai.

The manifest names a version, a package URL (a GitHub release asset) and its SHA-256. The plugin downloads the package itself and refuses it unless the file’s hash matches. A valid Site ID and key select the general or canary channel; the manifest itself carries no secret. Automatic installation follows NORG_EDGE_AUTO_UPDATE; with it off, the update appears on the Plugins screen for an administrator to approve. The status page reports when DISALLOW_FILE_MODS or AUTOMATIC_UPDATER_DISABLED prevents updates. Minimum WordPress 6.0 and PHP 7.4; no other runtime libraries.

Deactivation and uninstall

Deactivation stops everything; uninstall also removes every trace.

Deactivate removes the cron events, the entries it added to cache plugins, the .htaccess block if one was written, and its lock. Uninstall additionally deletes its options (settings included), the update-manifest transient and every _transient_norg_edge_* row. On request NORG deletes the site’s mirrors and record.

Questions, or the source before you run it

Write to solutions@norg.ai with your hostname and the section this is about. We will send the plugin source for the version you are being asked to install.