MetrikStack
Sources

WordPress

Browser script injection and server-side pageview tracking for WordPress, as a plugin.

The WordPress plugin mirrors the Laravel package: it can inject the browser tracker into wp_head, track pageviews server-side on template_redirect, or both, with the same request-id merging so both sources count as one pageview.

Requirements

  • PHP 8.1+
  • WordPress 6.4+

Installation

The plugin is not publicly downloadable during the private beta. It is available on request. Once you have it, activate it from the Plugins list.

Configure

Go to Settings → MetrikStack:

  • API endpoint: the MetrikStack API base URL. The default is https://api.metrikstack.com.

  • Site ID: the site's UUID from your analytics account.

  • Ingest token: the server-side ingest token; only needed for server-side tracking. Shown masked once saved.

  • Inject browser script: adds

    <script defer src="{endpoint}/js/script.js" data-site="{site id}"></script>

    to wp_head on every front-end page, the same tag as the browser script install.

  • Server-side tracking: defaults on the first time you save a token; toggle it off to run browser-only.

  • Logged-in users: off by default. When off, neither the browser script nor server-side tracking runs for logged-in WordPress users.

  • Admin & bot paths: on by default. wp-admin, the REST API, cron, feeds and previews are always excluded; this setting additionally excludes wp-login.php, xmlrpc.php, and requests with an approximate bot/crawler User-Agent. This is a cheap opt-in filter, not MetrikStack's own bot classification: bots are still detected and labeled from every source regardless of this toggle.

  • Behind a proxy: off by default. Enable only on a site that sits behind a reverse proxy or CDN you control, so server-side tracking reads the visitor IP from X-Forwarded-For (falling back to CF-Connecting-IP) instead of REMOTE_ADDR.

All fields go through the WordPress Settings API with nonces, the manage_options capability, and sanitize callbacks: the endpoint must be a valid URL, the site ID must look like a UUID, and invalid input is rejected with the previous value kept.

How merging works

On a trackable front-end request (GET/HEAD, Accept: text/html, non-asset path, front-end only, never admin/REST/cron/feeds/previews) the plugin generates a UUIDv7 request id on template_redirect, before any output, and sends:

  • X-MetrikStack-Request-Id: <id> response header
  • Server-Timing: metrikstack;desc=<id> response header
  • <meta name="metrikstack-request-id" content="<id>"> in <head>

exactly like the Laravel package and the Node SDK. The browser script reads the meta tag first, then falls back to performance.getEntriesByType('navigation')[0].serverTiming, which survives a CDN that caches the HTML (and the meta tag with it) but passes response headers through fresh.

Both sources then send the same request_id, and they are merged into one pageview: the site's configured primary source counts as the pageview, the other source contributes only the fields the primary lacks (time on page, scroll depth, screen size, language). See How merging works.

The headers and meta tag are emitted for every trackable page load regardless of whether Server-side tracking is switched on, so a site running the browser script only still cooperates with a server or edge source you add later in front of the same site.

What is sent

Server-side tracking sends exactly these fields, and nothing else, as a single-event batch to POST {endpoint}/v1/batch:

FieldSource
namealways pageview
urlfull URL, query string stripped to utm_* and ref only
referrerthe Referer header
request_idUUIDv7 generated per request
tsUTC timestamp
status_codehttp_response_code() at shutdown, or 200
methodGET / HEAD
ipREMOTE_ADDR, or X-Forwarded-For/CF-Connecting-IP when Behind a proxy is on; hashed and discarded at ingest
user_agentthe User-Agent header
accept_languagethe Accept-Language header

Never sent: cookies, session data, form/request bodies, post content, authenticated user identity, or the full query string.

Delivery

The event is POSTed with Authorization: Bearer <token> from the shutdown action, using wp_remote_post() with blocking => false and a 1 second timeout, after the response has already reached the client, so tracking never adds latency. There is no batching or retry: it is one non-blocking POST per tracked pageview.

Trade-off: this is simpler and adds no infrastructure, at the cost of one outbound request per pageview instead of amortizing several into a batch (as the Node SDK's in-memory buffer or the Laravel package's queued job do). If your traffic is high enough for that to matter, batch server-side events yourself with WP-Cron: write rows to a small custom table on shutdown, then POST them as one batch from a wp_schedule_event callback every minute or so.

Under most full-page-cache setups, a cached response never runs template_redirect, so a fully cached request produces no server-side event; only the browser script (baked into the cached HTML) fires. This matches the same limitation any origin-side integration has under a page cache.

Verify

From the MetrikStack CLI, once the plugin is active and has served at least one pageview:

metrikstack sources status --site <site-id>

Look for a server source with a recent last_event_at and, if the browser script is also enabled, a script source alongside it with a matching or near-identical pageview count for the same period, confirming the two sources are merging rather than double-counting.

Uninstalling

Deleting the plugin (not just deactivating it) removes its one option row, metrikstack_wp_options, including on every site in a multisite network (uninstall.php).

On this page