# WordPress Installation Guide

> Install Virent Human Analytics and AI Crawler Analytics on WordPress.

Canonical: https://stage.virent.app/docs/installation/wordpress
Last updated: 2026-08-26

This is the canonical Virent installation guide for WordPress sites.

### Install with your coding agent

Paste this prompt into Claude Code, Codex, Cursor or another coding agent.

```text
Install Virent analytics on my WordPress website.

Read the canonical guide first and follow its code exactly:
https://stage.virent.app/docs/installation/wordpress.md

Install two separate things:
1. Human Analytics (browser): Enqueue the Virent browser script through WordPress front-end hooks.
2. AI Crawler Analytics (server): Create a plugin that sends public crawler page requests to Virent.

Values I will give you (they are in Virent under Settings > Connection):
- Publishable browser key, starts with vha_pk_. Safe in browser code.
- Site ID (VIRENT_SITE_ID) and server secret (VIRENT_INGEST_SECRET, starts with vha_sk_). Server only.
Ask me for them. Never invent a key or use a masked preview such as vha_sk_••••.

Human Analytics best practices:
- Use wp_enqueue_scripts and script_loader_tag from a plugin or child theme instead of editing theme header files directly.
- Keep the publishable key in the script tag and the server secret out of browser output.
- Prefer a plugin or mu-plugin for portability across theme changes.

AI Crawler Analytics best practices:
- Install crawler telemetry as a small plugin or mu-plugin rather than editing WordPress core.
- Use wp_remote_post with a server-side secret stored in wp-config.php or environment variables.
- Skip wp-admin, REST API, cron, login, and static asset requests unless intentionally tracking them.

Rules:
- Keep VIRENT_INGEST_SECRET in server environment variables. Never put it in client code, public env variables (NEXT_PUBLIC_, VITE_, PUBLIC_) or committed files.
- Exclude API routes, static assets, images, favicons, robots.txt and sitemaps from crawler capture.
- If the site sends a Content-Security-Policy, allow https://app.virent.app in script-src and connect-src.
- Do not change unrelated code.

When you finish, list the files you changed and how I verify each path:
- View source on a public page and confirm the Virent script tag exists.
- Open the page in a browser and check Network requests.
- Refresh Human Analytics status.
- Activate the plugin or mu-plugin.
- Request a public page with curl -A "GPTBot".
- Refresh AI Analytics status.
```

## Research Notes

- WordPress front-end scripts should be registered through `wp_enqueue_scripts`.
- Custom script attributes can be added through `script_loader_tag`.
- The `shutdown` hook runs late enough to read the final WordPress response status.
- `wp_remote_post` performs server-side POST requests.

Primary references:

- https://developer.wordpress.org/reference/functions/wp_enqueue_script/
- https://developer.wordpress.org/reference/hooks/shutdown/
- https://developer.wordpress.org/reference/functions/wp_remote_post/

## Human Analytics

Prefer a small plugin or mu-plugin so tracking survives theme changes.

```php
<?php
/**
 * Plugin Name: Virent Human Analytics
 */

add_action('wp_enqueue_scripts', function () {
    wp_enqueue_script(
        'virent-human-analytics',
        'https://app.virent.app/js/script.js',
        array(),
        null,
        false
    );
});

add_filter('script_loader_tag', function ($tag, $handle) {
    if ($handle !== 'virent-human-analytics') {
        return $tag;
    }

	return str_replace(
		'<script ',
		'<script defer data-write-key="vha_pk_..." ',
		$tag
	);
}, 10, 2);
```

Best practices:

- Use `wp_enqueue_scripts` rather than editing WordPress core.
- Prefer a plugin, mu-plugin, or child theme over direct theme edits.
- Keep the server secret out of rendered HTML.
- Purge page caches and CDN caches after installation.

Verification:

- Activate the plugin.
- View source on a public page and confirm the Virent script exists.
- Check Network for `/js/script.js` and pageview requests.
- Refresh Human Analytics status.

Troubleshooting:

- If the script is absent, confirm the plugin is active and the hook runs on the front end.
- If page caching hides the change, purge cache and CDN.
- If a security plugin blocks the script, allow the Virent origin.

### First-party (recommended)

Ad blockers and privacy tools often block third-party analytics hosts, which silently drops visits. Relay the script and events through your own domain under `/_vr`, and load the script with `data-api="/_vr"` so events go to your domain too. The relay must forward the visitor's IP and country in `x-virent-forwarded-for` and `x-virent-forwarded-country`, with your website's secret key (`vha_sk_…`) in `x-virent-proxy-key`; without the key Virent ignores the forwarded headers and sees your server's network instead of your visitors'. Relay only `/_vr/js/script.js` and `/_vr/v1/*`, and never forward cookies or authorization headers. See [First-party analytics](/docs/installation/first-party) for how Virent trusts these headers.

With nginx, add these locations to the site's server block:

```text
location = /_vr/js/script.js {
  proxy_pass https://app.virent.app/js/script.js;
  proxy_set_header Host app.virent.app;
  proxy_ssl_server_name on;
  proxy_set_header Cookie "";
}

location /_vr/v1/ {
  proxy_pass https://app.virent.app/v1/;
  proxy_set_header Host app.virent.app;
  proxy_ssl_server_name on;
  proxy_set_header Cookie "";
  proxy_set_header Authorization "";
  proxy_set_header X-Virent-Forwarded-For $remote_addr;
  # Your website's secret key: Virent trusts the forwarded IP only with it.
  proxy_set_header X-Virent-Proxy-Key "vha_sk_...";
}
```

On Cloudflare, deploy a Worker on the `example.com/_vr/*` route that copies only `accept-language`, `content-type`, `origin` and `user-agent`, sets `x-virent-forwarded-for` from `cf-connecting-ip`, `x-virent-forwarded-country` from `request.cf.country` and `x-virent-proxy-key` from a Worker secret holding your website's secret key, and fetches the same path without `/_vr` from `https://app.virent.app`. The dashboard install panel has the full Worker.

Then load the script from your domain:

```html
<script
  defer
  data-api="/_vr"
  data-write-key="vha_pk_..."
  src="/_vr/js/script.js"
></script>
```

## AI Analytics

Keep server values in `wp-config.php` or environment-specific configuration.

```php
define('VIRENT_SITE_ID', 'site_...');
define('VIRENT_INGEST_SECRET', 'vha_sk_...');
define('VIRENT_INGEST_ENDPOINT', 'https://app.virent.app/v1/ingest/bot');
```

Install a plugin or mu-plugin:

```php
<?php
/**
 * Plugin Name: Virent AI Analytics
 */

add_action('shutdown', function () {
    if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
        return;
    }

    $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
    $path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';

    if (
        preg_match('/\.(?:png|jpg|jpeg|gif|svg|ico|webp|css|js|map|txt|xml)$/i', $path) ||
        ! preg_match('/GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|claudebot|Claude-Web|Claude-User|Claude-SearchBot|PerplexityBot|PerplexityBot\/|Perplexity-User|Perplexity-User\/|Google-CloudVertexBot|DeepSeekBot|DeepSeekSpider|CCBot|Bytespider|meta-externalagent|meta-webindexer|meta-externalfetcher|Amazonbot|DuckAssistBot|YouBot/i', $user_agent)
    ) {
        return;
    }

    wp_remote_post(VIRENT_INGEST_ENDPOINT, array(
        'headers' => array(
            'Content-Type' => 'application/json',
            'x-api-key' => VIRENT_INGEST_SECRET,
        ),
        'body' => wp_json_encode(array(
            'siteId' => VIRENT_SITE_ID,
            'url' => home_url(add_query_arg(array(), $_SERVER['REQUEST_URI'] ?? '/')),
            'host' => $_SERVER['HTTP_HOST'] ?? wp_parse_url(home_url(), PHP_URL_HOST),
            'path' => $path,
            'method' => $_SERVER['REQUEST_METHOD'] ?? 'GET',
            'referer' => $_SERVER['HTTP_REFERER'] ?? null,
            'userAgent' => $user_agent,
            'statusCode' => http_response_code() ?: 200,
            'requestId' => wp_generate_uuid4(),
            'timestamp' => gmdate('c'),
        )),
        'timeout' => 2,
        'blocking' => false,
    ));
});
```

Best practices:

- Use a plugin or mu-plugin rather than editing core.
- Skip `wp-admin`, AJAX, cron, login, REST API, and static asset requests unless intentionally tracking them.
- Store `VIRENT_INGEST_SECRET` outside rendered HTML.
- Confirm the host allows outbound HTTP requests to Virent.

Verification:

```sh
curl -A "GPTBot" https://your-domain.com/
```

- Confirm the plugin is active.
- Confirm Virent receives crawler events.
- Refresh AI Analytics status.

Troubleshooting:

- If no events arrive, confirm outbound HTTP is allowed by the host or security plugin.
- If admin requests are tracked, keep admin and login exclusions.
- If the secret appears in page source, move it to server config and rotate it.
