Skip to main content

Tracking

Burst Statistics tracks visitors and sessions by collecting page hits from the browser via JavaScript and processing them server-side. This document covers the full tracking lifecycle, session handling, IP processing, and all available extension points.


Overview

When a visitor loads a page, Burst enqueues a JavaScript tracking script (burst.min.js or burst-cookieless.min.js). The script sends a JSON payload to the server on each page view or time-on-page update. The server validates, sanitizes, and stores the hit in the burst_statistics and burst_sessions database tables.

Each hit is classified as either a create (new row in burst_statistics) or an update (accumulate time_on_page on an existing row). Session records in burst_sessions are created on the first hit and updated on subsequent hits within the same session window (30 minutes).


Tracking Endpoints

Burst supports two server-side collection mechanisms depending on the site's server configuration.

REST API Endpoint (default)

POST /wp-json/burst/v1/track
Content-Type: application/json

The request body must be a JSON-encoded string (double-encoded). The endpoint is registered via rest_api_init and has permission_callback set to __return_true (no authentication required). A { "request": "test" } body returns { "success": "test" } without recording a hit.

Beacon Endpoint (fallback)

When the REST API is unavailable, Burst falls back to a beacon endpoint that reads raw php://input. A body of request=test returns HTTP 200 without recording a hit.

The active endpoint is determined by Endpoint::get_tracking_status(). When the beacon endpoint is active, the tracking script does not depend on wp-api-fetch.


Hit Payload

The JavaScript client sends the following fields in the JSON payload:

FieldTypeDescription
urlstringFull URL of the current page, including query string
referrer_urlstringHTTP referrer URL
user_agentstringBrowser user-agent string
uidstringCookie-based unique visitor identifier
fingerprintstringCanvas/audio fingerprint (cookieless mode)
time_on_pageintSeconds spent on the current page
completed_goalsarrayArray of goal IDs completed client-side
page_idintWordPress post/term ID
page_typestringPage type identifier (see Page Type Identifiers)
should_load_ecommerceboolWhether to load ecommerce tracking

Tracking Lifecycle

Breaking change

As of v3.3.0 the IP-block check is performed at the very top of track_hit(), before prepare_tracking_data() runs. Previously the check was performed separately in each endpoint handler. Any custom code that relied on the check occurring after sanitization must be updated.

Browser sends POST payload


IP block check ──blocked──► return 'ip blocked'


prepare_tracking_data() — sanitize & parse user-agent


Custom block rules check ──blocked──► return 'blocked by custom rule'


Referrer spam check ──spam──► return 'referrer is spam'


get_hit_type() — determine 'create' or 'update', fetch last row


burst_before_track_hit filter


Session: create or update burst_sessions

├── hit_type = 'update' & same URL ──► update_statistic() (accumulate time_on_page)

└── hit_type = 'create' ──► burst_before_create_statistic action
► create_statistic()
► burst_after_create_statistic action


create_goal_statistic() (if goals completed)

Hit Type Determination

A hit is classified as an update when all of browser_id, browser_version_id, platform_id, and device_id are 0 — meaning the client did not send a user-agent (typically a heartbeat ping). The last matching row is fetched from burst_statistics for the same uid/fingerprint within the last 30 minutes.

ConditionResult
Last row found, all device IDs = 0update — accumulate time_on_page
Last row found, device IDs presentcreate — new pageview row
No last row found, all device IDs = 0Error — returns empty (no action)
No last row found, device IDs presentcreate — first visit

Session Handling

Sessions are stored in {prefix}_burst_sessions. A new session is created when no prior hit exists for the visitor within the last 30 minutes.

Session Table Schema

ColumnTypeDescription
IDintAuto-increment primary key
first_visited_urlTEXTFirst page URL in the session
last_visited_urlTEXTMost recently visited URL
hostvarchar(255)Site host (populated when domain filtering is enabled)
referrervarchar(255)Traffic source referrer
goal_idintAssociated goal ID
city_codeintGeoIP city lookup ID
browser_idintBrowser lookup table ID
browser_version_idintBrowser version lookup ID
platform_idintOS/platform lookup ID
device_idintDevice type lookup ID
first_time_visittinyint1 if this is the visitor's first visit
bouncetinyint1 if the session is still a bounce (single page)

Changed in v3.3.0: browser_id, browser_version_id, platform_id, device_id, first_time_visit, and bounce were moved from burst_statistics to burst_sessions. All session-level fields are now written exclusively to this table. See the Database Tables section for details.

A session is updated when the visitor navigates to a different URL within the same session window. The last_visited_url and bounce flag are updated accordingly.

Multi-Domain Tracking

Burst detects multi-domain setups automatically. The first domain seen is stored in burst_first_domain. If a subsequent hit comes from a different hostname, burst_is_multi_domain is set to true. When the Filter by domain setting is enabled, the host column is populated on sessions to allow per-domain filtering.


IP Processing

IP address resolution is handled by Burst\Frontend\Ip\Ip. The following headers are checked in priority order:

PriorityHeaderSource
1HTTP_CF_CONNECTING_IPCloudflare real client IP
2HTTP_TRUE_CLIENT_IPAkamai / Cloudflare Enterprise
3HTTP_X_FORWARDED_FORReverse proxies (leftmost public IP used)
4HTTP_X_REAL_IPnginx proxy
5HTTP_X_CLUSTER_CLIENT_IPCluster environments
6HTTP_CLIENT_IPGeneral proxy header
7REMOTE_ADDRDirect connection

Public (non-private, non-reserved) IPs are preferred. Both IPv4 and IPv6 are supported, including IPv4-mapped IPv6 addresses.

IP Blocklist

IPs are blocked before any data is recorded. The blocklist is configured under Settings → IP Blocklist (ip_blocklist option) as one IP or CIDR range per line. Both IPv4 and IPv6 CIDR notation is supported (e.g., 192.168.1.0/24, 2001:db8::/32).

Test Hit Bypass

A request can bypass the IP block only when the URL contains burst_test_hit and includes a valid nonce query parameter that matches the transient stored under burst_onboarding_token.

Changed in v3.3.0: Previously, any URL containing burst_test_hit bypassed the IP block. A nonce check is now required to prevent unauthorized bypass of the IP block.

# URL that will bypass the IP block check (nonce must match burst_onboarding_token transient)
https://example.com/?burst_test_hit=1&nonce=<valid-nonce>

Visitor Identification

Burst supports two visitor identification modes:

A uid value is generated by the JavaScript client and stored in a browser cookie. The cookie retention period defaults to 30 days and is configurable via the burst_cookie_retention_days filter.

Cookieless (fingerprint)

When cookieless tracking is enabled, a browser fingerprint (canvas/audio based) is used as the uid. The fingerprint is also stored in a PHP session ($_SESSION['burst_fingerprint']) when server-side goals or ecommerce tracking is active.

caution

PHP sessions for fingerprint storage fall back to the WordPress uploads directory (wp-content/uploads/burst-sessions/) if the default session save path is not writable.


User-Agent Parsing

Browser, browser version, platform, and device type are extracted from the User-Agent header by Burst\UserAgentParser\UserAgentParser. The results are stored in normalised lookup tables:

  • {prefix}_burst_browsers
  • {prefix}_burst_browser_versions
  • {prefix}_burst_platforms
  • {prefix}_burst_devices

Lookup table IDs are cached in memory per request and via the WordPress object cache (burst cache group, key burst_{item}_all).


Page Type Identifiers

The page_type field is inserted into the <body> tag as a data-burst_type attribute by PHP output buffering. Valid values are:

ValueDescription
front-pageStatic homepage
blog-indexPosts homepage
date-archiveDate archive
404Not found page
archive-genericGeneric archive
wc-shopWooCommerce shop page
tagTag archive
taxCustom taxonomy archive
authorAuthor archive
searchSearch results
categoryCategory archive
Any public post type sluge.g., post, page, product

The page ID (data-burst_id) and type are injected into the opening <body> tag using output buffering (ob_start/ob_end_flush).


Custom Block Rules

Settings → Custom Block Rules (custom_block_rules option) accepts one rule per line. Rules are matched against the page URL, referrer, and user-agent. Rules can be plain strings (case-insensitive substring match) or regex patterns.

Regex pattern format: Must start and end with / and may include flags (i, m, s, x, u).

# Plain string — blocks any URL, referrer, or UA containing this text
example-spam.com

# Regex — blocks URLs ending in a number
/text-in-url[0-9]+/i

# Regex — blocks all traffic from a specific domain
/^https:\/\/domain\./

# Regex — blocks known bots
/facebook(bot|crawler)/i

Invalid regex patterns are logged and skipped. Block rule matches are logged when BURST_DEBUG is defined and truthy.


Excluding Visitors from Tracking

By User Role

Logged-in users can be excluded by role via Settings → User Role Blocklist (user_role_blocklist option). The roles list is filterable:

burst_roles_excluded_from_tracking

Filter the list of WordPress user roles that are excluded from tracking.

Parameters:

ParameterTypeDescription
$rolesarrayArray of role slugs excluded from tracking

Example:

add_filter( 'burst_roles_excluded_from_tracking', function( $roles ) {
$roles[] = 'editor';
return $roles;
} );

By Headless Mode

Setting headless to true in Burst options (or defining the constant BURST_HEADLESS) prevents the tracking script from being enqueued entirely.

Page Builder Previews

Tracking is automatically suppressed when the current request is detected as a page builder or plugin preview.


Hooks and Filters Reference

burst_before_track_hit

Fires after data sanitization but before session handling and database writes. Use this hook to modify tracking data or abort tracking by returning modified data.

Parameters:

ParameterTypeDescription
$sanitized_dataarraySanitized tracking data (see payload fields)
$hit_typestring'create' or 'update'
$previous_hitarrayPrevious hit row from burst_statistics, or empty array

Example:

add_filter( 'burst_before_track_hit', function( $data, $hit_type, $previous_hit ) {
// Add a custom field or modify the data before it is stored.
return $data;
}, 10, 3 );

burst_before_create_statistic

Fires immediately before a new row is inserted into burst_statistics.

Parameters:

ParameterTypeDescription
$statisticarrayStatistic data about to be inserted

Changed in v3.3.0: $statistic no longer contains session-level fields (bounce, browser_id, browser_version_id, platform_id, device_id, referrer, city_code, first_time_visit). These fields are now written exclusively to burst_sessions before this action fires.

Example:

add_action( 'burst_before_create_statistic', function( $statistic ) {
// Inspect or log data before insert.
// Note: session-level fields (browser_id, device_id, etc.) are not present in $statistic.
} );

burst_after_create_statistic

Fires immediately after a new row is inserted into burst_statistics.

Parameters:

ParameterTypeDescription
$insert_idintID of the newly created statistic row
$statisticarrayStatistic data that was inserted

Changed in v3.3.0: $statistic no longer contains session-level fields (bounce, browser_id, browser_version_id, platform_id, device_id, referrer, city_code, first_time_visit). These fields are now written exclusively to burst_sessions.

Example:

add_action( 'burst_after_create_statistic', function( $insert_id, $statistic ) {
// React to a new pageview being recorded.
// Note: session-level fields (browser_id, device_id, etc.) are not present in $statistic.
}, 10, 2 );

burst_tracking_options

Filter the options array that is passed to the frontend JavaScript via wp_localize_script. The returned value populates the global burst JavaScript object.

Parameters:

ParameterTypeDescription
$optionsarrayFull tracking options array

Example:

add_filter( 'burst_tracking_options', function( $options ) {
// Disable debug mode regardless of BURST_DEBUG constant.
$options['options']['debug'] = 0;
return $options;
} );

The $options array structure:

[
'tracking' => [
'isInitialHit' => true,
'lastUpdateTimestamp' => 0,
'beacon_url' => 'https://example.com/path/to/beacon',
'ajaxUrl' => 'https://example.com/wp-admin/admin-ajax.php',
],
'options' => [
'cookieless' => 0, // 1 = cookieless mode
'pageUrl' => 'https://…',
'beacon_enabled' => 0, // 1 = use beacon endpoint
'do_not_track' => 0, // 1 = respect DNT header
'enable_turbo_mode' => 0, // 1 = defer script
'track_url_change' => 0, // 1 = track SPA navigation
'cookie_retention_days' => 30,
'debug' => 0,
],
'goals' => [
'completed' => [],
'scriptUrl' => 'https://…/burst-goals.js?v=…',
'active' => [],
],
'cache' => [
'uid' => null,
'fingerprint' => null,
'isUserAgent' => null,
'isDoNotTrack' => null,
'useCookies' => null,
],
]

Filter the number of days the visitor UID cookie is retained.

Parameters:

ParameterTypeDescription
$daysintCookie lifetime in days (default: 30)

Example:

add_filter( 'burst_cookie_retention_days', function( $days ) {
return 365; // Keep cookie for one year.
} );

burst_goals_script_url

Filter the URL of the goals JavaScript bundle.

Parameters:

ParameterTypeDescription
$urlstringFull URL to burst-goals.js

Example:

add_filter( 'burst_goals_script_url', function( $url ) {
return 'https://cdn.example.com/burst-goals.js';
} );

burst_script_dependencies

Filter the script handles that the main tracking script depends on.

Parameters:

ParameterTypeDescription
$depsarrayArray of registered script handles

Example:

add_filter( 'burst_script_dependencies', function( $deps ) {
$deps[] = 'my-custom-script';
return $deps;
} );

burst_ip_blocklist

Filter the array of blocked IP addresses and CIDR ranges before the current visitor's IP is checked.

Parameters:

ParameterTypeDescription
$blocked_ipsarrayArray of IP addresses and CIDR ranges

Example:

add_filter( 'burst_ip_blocklist', function( $blocked_ips ) {
$blocked_ips[] = '203.0.113.0/24'; // Block an additional CIDR range.
return $blocked_ips;
} );

burst_visitor_ip

Filter the resolved visitor IP address before it is used for blocklist checks.

Parameters:

ParameterTypeDescription
$ipstringResolved IP address (may be empty string if unresolvable)

Example:

add_filter( 'burst_visitor_ip', function( $ip ) {
// Override IP resolution for requests behind a trusted proxy.
if ( ! empty( $_SERVER['HTTP_X_CUSTOM_IP'] ) ) {
return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_CUSTOM_IP'] ) );
}
return $ip;
} );

burst_obfuscate_filename

Filter whether ghost mode (filename obfuscation) is active. When truthy, all tracking assets are renamed with a b- prefix and served from the uploads directory.

Parameters:

ParameterTypeDescription
$enabledboolWhether ghost mode is active

Example:

add_filter( 'burst_obfuscate_filename', '__return_true' );

Settings Reference

The following plugin settings directly affect tracking behaviour:

Option keyTypeDescription
enable_cookieless_trackingboolUse fingerprinting instead of cookies
enable_do_not_trackboolHonour the browser's DNT: 1 header
enable_turbo_modeboolLoad tracking script with defer (in footer) instead of async
track_url_changeboolTrack SPA URL changes (query string included in URL comparison)
filtering_by_domainboolStore and filter by host in sessions
custom_block_rulesstringNewline-separated block rules (strings or regex)
ip_blockliststringNewline-separated IP addresses or CIDR ranges
user_role_blocklistarrayWordPress user roles excluded from tracking
ghost_modeboolObfuscate tracking asset filenames
headlessboolDisable script enqueueing (API-only mode)
combine_vars_and_scriptboolInline tracking options into a single pre-built JS file

Debug Mode

Define BURST_DEBUG as true in wp-config.php to enable verbose logging:

define( 'BURST_DEBUG', true );

When debug mode is active:

  • Custom block rule matches are logged.
  • Failed statistic inserts/updates are logged.
  • Client-side HTTP 400 tracking errors are accepted via the burst_tracking_error AJAX action and written to the error log.
  • The debug: 1 flag is included in the JavaScript burst options object.

Database Tables

TablePurpose
{prefix}_burst_statisticsOne row per pageview. Foreign key to burst_sessions.
{prefix}_burst_sessionsOne row per visitor session.
{prefix}_burst_goal_statisticsMany-to-many join between statistics rows and completed goals.
{prefix}_burst_browsersBrowser name lookup table.
{prefix}_burst_browser_versionsBrowser version lookup table.
{prefix}_burst_platformsOperating system lookup table.
{prefix}_burst_devicesDevice type lookup table.

Lookup tables are populated automatically on the first hit containing a new value. IDs are cached in the WordPress object cache under the burst group.

Breaking change

As of v3.3.0 the columns browser_id, browser_version_id, platform_id, device_id, first_time_visit, and bounce have been removed from burst_statistics and are stored exclusively in burst_sessions. A background database migration (move_columns_to_sessions) runs automatically on upgrade. Any custom SQL queries or third-party integrations that read these columns directly from burst_statistics must be updated to join burst_sessions instead.