PBJ.TECH — WordPress Plugins

PBJ Analytics — User Guide

Version 1.3.0 · pbj.tech/wordpress-plugins

PBJ Analytics — Install & Usage

What this plugin does

PBJ Analytics records website traffic — pageviews, sessions, navigation paths, entry/exit pages, time on page, scroll depth, referrers (with full URL, UTMs, and channel classification), internal site search, outbound clicks, 404s, regions/cities, languages, screen sizes, new vs returning visitors, funnels, goals, and custom events — into your own WordPress database and shows it on a charted dashboard inside wp-admin. No data is sent to Google Analytics or any external analytics service. Human traffic is tracked with a tiny first-party JavaScript beacon, so views are counted correctly even when pages are served from a full-page cache. Bots and AI crawlers (which rarely run JavaScript) are recorded separately on the server and reported in their own dashboard section, so they never inflate your human numbers.

Requirements

Installation

  1. In wp-admin, go to Plugins → Add New → Upload Plugin.
  2. Choose pbj-analytics.zip and click Install Now.
  3. Click Activate.
  4. On activation the plugin creates its three database tables (pbj_analytics_hits, pbj_analytics_daily, pbj_analytics_funnel_daily) and schedules nightly maintenance jobs. A new Analytics item appears in the admin sidebar.

Settings carry over on upgrades. Upgrading from a previous version (1.0 or 1.1) is automatic: the schema is migrated to db version 3 on first load.

First-time setup

Open Analytics → Settings. Every option is described below in the order it appears.

Data retention

Keep raw hit data for (days). How long individual hit rows are retained before the nightly job deletes them. Daily summary totals are kept forever regardless, so shortening this only affects how far back you can drill into per-hit detail and how far back the per-page drill-down, time-on-page report, and live-period KPIs can read. Default is 90 days.

Who to count

Count logged-in users. Off by default, so your own browsing and your team's don't inflate the numbers.

Exclude roles. When logged-in tracking is on, visits from the roles checked here are still never counted. Administrator and Editor are excluded by default.

Do Not Track. When on, visitors whose browser sends the Do Not Track header are not counted. On by default.

What to capture

Engagement → Time on page. When on, the beacon sends a second request when the page is closed/navigated away with the elapsed milliseconds, so the dashboard can show average time on page. On by default.

Engagement → Scroll depth. When on, the beacon records scroll-depth events at 25/50/75/100 % milestones. On by default.

Marketing attribution → Capture UTM parameters. When on, utm_source, utm_medium, utm_campaign, utm_term, utm_content are pulled from each pageview URL and stored. On by default. Channels (Direct / Organic Search / Paid / Social / Email / Referral) are classified server-side from the referrer + UTM medium regardless of this setting.

Visitors → Distinguish new vs returning. When on, the plugin maintains a separate longer-rotated salted hash (no cookies) so it can tag each pageview as new or returning. On by default. Tunable rotation window (default 7 days). Toggle off for strictest privacy.

Region & city. Off by default. When on, the same ip-api fallback that already resolves country also returns region/state and city, and the dashboard adds Regions and Cities tables. Region and city are only useful if "Resolve visitor country" below is also on.

Language & screen. Browser language (from navigator.language) and screen-size bucket (sm/md/lg/xl from window.innerWidth). Both on by default.

Events & goals

Custom events. When on, the beacon auto-records outbound link clicks and any element carrying a data-pbj-event attribute. On by default.

Goals. A list of event labels (one per line). Events whose label exactly matches one of these are counted toward the Goal completions KPI on the dashboard.

Funnels

Each funnel needs an ID (slug), a name, and 2+ steps. Each step is one line in the form type|value:


url|/pricing
url|/signup
event|signup_complete

URL steps support a trailing for prefix matches: url|/shop/. Funnel rollups run nightly and on dashboard load for today + yesterday.

Bots & AI crawlers

Track bots. When on, the plugin records bot and AI-crawler page loads on the server and reports them in the dashboard's "Bots & AI crawlers" section. On by default. Kept entirely separate from human pageview totals.

Geolocation

Resolve visitor country. When on, uses Cloudflare's country header if present; otherwise a cached lookup against ip-api.com. The only outbound network request the plugin makes. Turn off to keep the plugin fully offline.

Geo cache (minutes). Default 1440 (24 hours).

Weekly email digest

Enabled. When on, the plugin emails a weekly HTML summary via wp_mail. KPIs with week-over-week deltas, top pages / referrers / channels / countries, bot totals.

Recipient. Where to send the digest. Defaults to the site admin email if blank.

Day of week. Which day the digest fires (default Monday). Use Send test digest now to preview.

Public read-only dashboard

Enabled. When on, exposes a read-only summary at a tokenized URL: /wp-json/pbj-analytics/v1/public/{token}. No login required.

URL. Click-to-select the full URL. Regenerate token invalidates the old URL immediately.

Click Save settings.

Daily usage

Open Analytics → Dashboard. At the top, pick a range: Last 7, 30, 90, or 365 days. Every headline KPI shows the delta vs the previous equal-length period. The dashboard shows (in order):

Funnels. If you've configured any, each appears as its own card with step counts and conversion %.

Bots & AI crawlers. Below the human reports: total bot hits, AI-crawler hits, by-category doughnut, ranked table of specific bots seen, and the top URLs each bot is fetching.

Live. The Analytics → Live sub-page shows last-5-minutes pageviews, a per-minute bar chart, and the latest 15 hits — auto-refreshing every 10 seconds.

Page drill-down. Click any URL on the Dashboard to open a page-detail report: pageviews, unique visitors, average time, average scroll depth, plus per-page referrers / channels / countries / devices.

CSV export. Tables with a CSV link in the header (Top pages, Top referrers, etc.) download the full 500-row breakdown for the current range.

Recording a custom event. Add a data-pbj-event attribute to any clickable element:


<a href="/pricing" data-pbj-event="Pricing CTA">See pricing</a>
<button data-pbj-event="Newsletter signup">Subscribe</button>

Outbound links (links to other domains) are tracked automatically as outbound: example.com with no markup needed. Scroll-depth events show up as scroll: 25%, etc.

Verifying tracking works

  1. Open your site's front end in a logged-out browser or a private/incognito window (your own logged-in admin visits are not counted by default).
  2. Visit a few pages, scroll, and close the tab.
  3. Back in wp-admin, open Analytics → Dashboard and refresh. The visits appear immediately — the dashboard rolls up the current day on load.
  4. Open Analytics → Live to see real-time hits.

If you see nothing, work through Troubleshooting below.

Upgrading

Manual upgrade: deactivate is not required. Go to Plugins → Add New → Upload Plugin, upload the newer pbj-analytics.zip, and choose Replace current with uploaded when prompted. Your settings and all collected data are preserved. Schema migrations run automatically on first load after the upgrade.

Automatic updates: the plugin ships with a self-hosted update channel (the Update URI header points at pbj.tech). Once a JSON update manifest is published at that URL, WordPress will offer updates on the Plugins screen like any other plugin. Until then, upgrade manually.

Troubleshooting

The dashboard shows no data. Confirm you are testing as a logged-out visitor (logged-in users are not counted unless you enabled that setting). Check that your theme calls wp_footer() — the beacon is enqueued through the standard script queue and needs the footer hook to print. Open your browser's Network tab and confirm a request to /wp-json/pbj-analytics/v1/collect fires on page load.

Engagement (time on page / scroll) shows zero everywhere. The engagement beacon is a second request fired on pagehide/visibilitychange. If your firewall or security plugin blocks /wp-json/pbj-analytics/v1/engagement, time-on-page and max-scroll won't be recorded (the pageview itself still counts).

Country column is always empty. Either country resolution is off (Analytics → Settings), or your server can't reach ip-api.com and you're not behind Cloudflare. If you don't need country data, leave it off.

Region / City tables are empty. Both require Region/City capture to be on AND country resolution to be on (they use the same ip-api lookup with extra fields).

Bounce rate is 0%. Bounce rate is computed from sessions in the raw hits table. If the selected range extends past your retention window, only sessions that fit inside the window can be counted — pick a shorter range or increase retention.

Numbers look low compared to my host's stats. That's expected and correct. Server log stats count bots, prefetchers, and asset requests; PBJ Analytics counts human pageviews and deliberately excludes a large list of bots and crawlers, plus logged-in staff. The excluded bots are not lost — they're reported in the separate "Bots & AI crawlers" section.

The Bots & AI crawlers section is empty. Either "Track bots" is off, or no recognized crawler has hit the site in the selected period. Bot hits are recorded server-side on real front-end page loads; requests to robots.txt, feeds, the REST API, and admin-ajax are intentionally not counted.

Counts didn't update / old data isn't being pruned. Maintenance, funnel rollups, and the weekly digest all run via WP-Cron (pbj_analytics_daily_maintenance, pbj_analytics_funnel_rollup, pbj_analytics_weekly_digest). The dashboard always rolls up the current and previous day on load, so recent figures are live regardless. Pruning, older-day rollups, the funnel rollup, and the digest depend on WP-Cron — if you've set DISABLE_WP_CRON, add a real system cron hitting wp-cron.php.

The public dashboard URL returns a 404. Either the feature is off, or the token in your URL doesn't match the current token. Regenerating the token invalidates the previous URL.

I deactivated the plugin and my settings/data are still there. By design. Deactivation only unschedules the crons and clears short-lived caches. To remove everything, Delete the plugin from the Plugins screen — that runs the uninstaller, which drops the tables and deletes all options.

Tracking on a heavily cached site. The beacon is JavaScript that runs in the visitor's browser, so it counts views even on fully cached HTML. If you serve a cached version of wp-admin (you shouldn't), the dashboard could look stale — exclude wp-admin from your page cache.

Where things live