MetrikStack
Sources

WordPress

WordPress analytics without cookies. The MetrikStack plugin adds the browser script, tracks pageviews server-side, or both, and counts each view once.

The WordPress plugin works like the Laravel package. It can add the browser script to wp_head, track pageviews server-side on template_redirect, or both. With both on, request ids make sure each page load counts as one pageview.

Requirements

  • PHP 8.1+
  • WordPress 6.4+

Installation

The plugin is not publicly downloadable; 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 quick filter in the plugin. MetrikStack's own bot classification still labels bots from every source, whatever this setting.

  • 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 and the site ID must look like a UUID; invalid input is rejected and 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>

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. That still works behind a CDN that caches the HTML (and the meta tag with it) but sends fresh response headers.

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

The headers and meta tag are sent on every trackable page load, whether or not Server-side tracking is on. A site that runs only the browser script today can add a server or edge source later without changes.

What is sent

Server-side tracking sends 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 reached the visitor, so tracking adds no latency. There is no batching or retry: each tracked pageview is one non-blocking POST.

This keeps the plugin simple and needs nothing extra on your server, but it makes one outbound request per pageview, where the Node SDK and the Laravel package send several events in one batch. 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.

With most full-page caches, a cached response never runs template_redirect, so it produces no server-side event; only the browser script in the cached HTML fires. Every integration that runs on the origin has this limitation under a page cache.

Verify

Once the plugin is active and has served at least one pageview, check the site's Sources page in the dashboard, or run this with the MetrikStack CLI:

metrikstack sources status <site>

Look for a server source with a recent last event. If the browser script is enabled too, the script source next to it should show about the same pageview count for the same period, which means the two are merged rather than counted twice.

Uninstalling

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

On this page