Analytics
Traffic and event reports kept inside WordPress with no cookie and no IP address stored, plus the same events sent to any destination add-on you install.
Analytics keeps this site's own record of its traffic and provides XPAC → Analytics in WordPress. Overview is the report: how much traffic there was, where it came from, what was read, what visitors did on the page — the links they followed, the files they downloaded, the media they played — and what they were using, and any row on it narrows the whole screen to that answer. Live shows who is on the site right now, Sessions lists the visits themselves, and Goals turns the events and pages you care about into conversions. The Events, Tools and Settings screens describe the event pipeline that feeds them, all from data that never leaves WordPress.
Nothing needs to be connected for the reports to fill up: the site's own record is on by default, sets no cookie, stores no IP address, and uses an identifier rebuilt daily, which is why it needs no consent prompt. How long history is kept is a setting under Settings → General, and it defaults to keeping everything.
Read this site's own reports
- Activate Analytics and open XPAC → Analytics → Overview.
- Visit the public site in a browser that is not signed in as an excluded role.
- The visit appears immediately: on Live within 15 seconds, and on Overview. The
Sources, Pages and Technology cards fill from the first visit; the
Campaigns tab under Sources fills once a visitor arrives through a link carrying
utm_source,utm_mediumorutm_campaign. - The Behaviour card fills as visitors interact: every event in the table below is counted, and its details list what each one happened to most — the links an outbound click followed, the files downloaded, the forms sent, the videos played.
The sidebar groups the screens as Monitor (Overview, Live, Sessions), Pipeline (Events, Tools) and Configuration (Goals, Settings).
Dates, comparison and interval
The range button in the bar opens the presets — Today, Yesterday, Last 7 days, Last 30 days (the default), Last 90 days, Month to date, Last month, Year to date, Last 12 months — and From and To pickers for any other range. Days follow the site's own clock, today included. The arrows beside it step the range back or forward by its own length, never past today.
Compare puts a second period behind the chart as a dashed line and shows the change beside every headline figure as an arrow and a percentage (↓ 24%). The choices are Previous period (the default), Year over year, a Custom… start and Off. Match day of week, on by default for ranges of a week or more, lines Mondays up with Mondays.
Interval groups the chart by Hour, Day, Week or Month, offering only the ones that fit the range: hours for ranges of up to seven days, as far back as the site keeps individual visits. By the hour the chart has views, visits, events, bounce rate and visit duration for each hour, counted from the visits that started in it; the headline figures and the cards still cover whole days. Daily visitors are counted per day, so that figure has no hourly line, and the hours still to come today are left empty rather than drawn as zero.
The six headline figures
| Figure | What it counts |
|---|---|
| Views | Pages opened. Its hint carries views per visit. |
| Visits | Sessions, closed after 30 idle minutes. |
| Daily visitors | People, counted fresh each day. Over a range this is a sum of daily figures: someone who came back on another day is counted again. The site cannot link a visitor across days, which is the same mechanism that lets it store no IP address. |
| Bounce rate | The share of visits that saw exactly one page. |
| Visit duration | From a visit's first hit to its last. |
| Events | Clicks, downloads, media, searches and the rest of the catalogue. |
Pressing a figure (or its number key, 1 to 6) draws it in the chart. A figure that
is a rate — bounce rate, visit duration — has no value at all on a day with no
visits, so the chart draws no mark there and the tooltip says so; it is not drawn as
zero. Filtered to one page, the figures become Views, Entries, Exits, Time on page,
Bounce rate and Events; filtered to a goal, Visit duration and Events become
Converting visits and CR (conversion rate).
Daily visitors, bounce rate and visit duration are counted from the release that added them; exit rate and each source's own bounce rate and duration from the first full day after the 1.3.0 schema update: the day after it, or the same day on a site that had recorded nothing yet that day. Over a range reaching back before that, the line under the bar says which day they start, and the figure shows a dash rather than a guess.
The Pages card separates what visitors read from what they were looking for: Pages, Entry (where visits started), Exit (where they ended, with the exit rate), Searches, Not found and Types. A search term or a 404 path is scrubbed of anything that looks personal — email addresses, phone numbers, tokens — before it is stored.
Filter by clicking a row
Every row on every card is a button, except a search: a search keeps the home page's address, so no filter selects one search term. Clicking a row adds a filter, shown as a chip under the bar — Channel is Search, Device is Mobile, Page is /pricing/ — and every figure, the chart, the comparison and every card are recomputed for just those visits. Cmd-click or Ctrl-click a page or referrer row to open its address instead.
- Click a chip to change it: the dimension, the operator (is, is not, and for pages, referrers and campaign fields contains and does not contain) and one or more values. Several values in one chip mean any of them; several chips mean all of them.
- Filter in the bar adds a chip without clicking a row, with suggestions for each
dimension. Clear removes them all, and so does
Esc. - The filters live in the page address, so a filtered view can be bookmarked or sent to another administrator.
The dimensions are Page, Entry page, Exit page, Page type, Channel, Referrer, Source,
Medium, Campaign, Term, Content, Device, Browser, OS, Event and Goal. A filter selects
visits: Page is /pricing/ keeps visits that viewed that page, Entry page the ones
that started there, Exit page the ones that ended there, and Event the ones in which the
event happened. A page, entry page or exit page filter leaves out search results, which
keep the home page's address: Page is / means the home page itself, and searches are
filtered with Page type is Search. Filtered to one page, the Pages card gains Next
pages and Previous pages: where visits went after it and where they came from.
A Goal chip works differently from the others. Goal is keeps every visit and marks the ones that converted, because a conversion rate needs all of them: the figures read Visits, Converting visits and CR, and every row on the cards shows its own conversions and CR. Goal is not keeps only the visits that did not convert.
A filtered view is computed from the individual visits rather than the daily totals, so it is bounded. A filter on Device, Browser, OS, Channel, Page type or Goal alone covers at most the last 31 days of the range, any other filter the last 92, and none reaches further back than the individual visits are kept; the line under the bar names the days a filtered view covers. If a range is still too large, the screen says Too slow for this range and offers a shorter one. Filtered, Daily visitors is counted from the visits themselves (point at the label for the note) and can differ slightly from the unfiltered figure.
Details, exports and notes
- Tabs. A card shows one tab at a time. A tab opened for the first time loads its rows while only that card dims; going back to a tab is instant. Campaign Term and Content are counted from the individual visits, so without a filter they cover at most the last 31 days of the range, and a line under the rows names the days.
- Details. Each card's details button opens the whole list: search it, sort by any column, page through 100 rows at a time, add a second dimension (channel, source, device, browser, OS or entry page), and Download CSV (up to 1,000 rows). A column appears only when at least one row in the list has a figure for it, so a figure this site cannot count yet — exit rate before its counter started — is left out rather than shown as a column of dashes; a dash in a column the list does carry means that row has no figure for it. Names read from the left and figures from the right. With a comparison on, each figure shows its change under it. Clicking a row there filters too.
- The chart. Point at a bar for its figures and the comparison. Click a day to open it by the hour, a week or month to open it by the day, or drag across the chart to zoom to that span. Show as a table lists every point and has its own Download CSV.
- Notes. Mark a day with a note from More → Add note…, by right-clicking the
chart, or with
non a focused bar; notes show as ticks under the chart. Automatic notes mark the day an XPAC plugin was updated, a capture or privacy setting changed, or retention removed history; turn them off under Settings → General. A note can be edited or deleted by its author or any administrator. Notes never filter anything. - On a narrow window a card drops the grey note beside its rows — the time on a page, an event's top value — from every row of that card at once, never from some of them, so a row with nothing beside it never reads as a row with nothing to say. The note is still in the row's details and in what a screen reader announces.
- Active hours shows visits by weekday and hour, in the site's timezone. It fills in just after the rest of the screen and reads at most the last 92 days of the range; over a longer range the line under it names the days it covers.
- Appearance. More → Appearance (or
d) switches between light and dark for this browser only. The chart, the bars and the selection follow the accent of your WordPress admin colour scheme (blue by default; a lighter blue in dark). The previous period is a dashed grey line. Beside a figure, green marks a change for the better and red one for the worse; no chart or bar is ever red. - Keyboard.
?lists every shortcut:t y w m q lpick a range andcopens the pickers,←→step the period,xtoggles the comparison,ichanges the interval,1–6pick a figure,rrefreshes, andg o,g l,g s,g g,g ejump to Overview, Live, Sessions, Goals and Events.
Live
Live answers "who is here now". On site now counts visits with a hit in the last
five minutes; Visits, Views and Events cover the last 30 minutes, with a bar
for each minute's views. Pages now lists the pages open in those visits, beside the
Sources, Events and Devices of the last 30 minutes. Each card names its own
window in its header — Last 5 minutes over Pages now, Last 30 minutes over the
other three — so no figure is read against the wrong clock. The screen updates every
15 seconds while its tab is visible; Pause (or p) holds it, and after three failed
updates in a row it stops and offers Retry. Clicking a row, other than a search,
opens Overview for today, filtered to that row. Filters never apply to Live. Overview's
bar shows the same count (12 on site now, or ● 12 when the bar is short of
room), and pressing it opens Live.
Sessions
Sessions lists visits, newest first: when each started, its entry page, source, device, browser and OS, views, duration, events (marked when the visit reached a goal), exit page and which visit of the day it was. It covers up to 92 days at a time (the last 7 by default), and the arrows beside the range step it like Overview's. Filter adds the same chips as on Overview, plus Views at least and Duration at least; the search box looks through entry pages and referrers. Under a Goal is chip it lists only the visits that converted, so its total matches Overview's Converting visits. The total counts up to 10,000 and then reads "10,000+"; Export, which names the count (Export 158 visits), downloads up to 10,000 visits as CSV, and the refresh button beside it is the same quiet one Overview uses.
Opening a visit shows, first, what a visit is — one browser on one day; nothing links a
person across days, and no address is kept — then its facts, such as Visit 6 from
this browser today, and a timeline: each page with its time on page and engaged time,
the events that happened on it, and the last page marked Exit page, or Last page
so far while the visit could still continue, with a line under the timeline saying why
(This visit may still go on: its last page was opened less than 30 minutes ago.). j
and k move to the next and previous visit.
Goals
A goal is an event from the catalogue, optionally narrowed to a value (the file
downloaded, the link followed) that is or contains some text, or a page whose
path is, starts with or contains some text. A page goal never counts a search
result, which keeps the home page's address, so a goal on / converts visits that
reached the home page, not visits that only searched. Goals lists each one with its
converting visits and conversion rate over the last 30 days. A visit converts at most
once per goal, and the rate is converting visits over all visits in the same range and
filters. Goals on scroll depth and outbound clicks count at most the last 31 days of a
range; any other goal the last 92.
Filter Overview by this goal opens Overview with a Goal is chip: the visits stay the same, the figures add Converting visits and CR, every card shows conversions and CR beside its rows, and Sessions lists the converting visits. Overview's Behaviour → Goals tab lists every goal for the current range, filters included. Up to 50 goals can be saved. If another administrator saved the goals while you were editing, saving says so and Reload keeps your draft.
What this report cannot tell you
Two things, and each is a property of how the record is kept rather than a missing feature:
- Where visitors are. No IP address is stored and there is no geography database, so there is no country or city to report.
- New against returning visitors. The daily identifier is deliberately unlinkable across days, so there are no returning visitors, cohorts or cross-day journeys.
Filtered views, hourly charts, Live, Sessions and goals read the individual visits, so they reach back only as far as Keep this site's own traffic history for keeps them.
What is captured
| Event | Trigger |
|---|---|
page_view | Page initialization |
search | A configured search query parameter or WordPress search results |
scroll_depth | Reaching a configured page scroll percentage |
click | Following an outbound HTTP or HTTPS link |
contact | Activating an email, telephone or SMS link; records the method without the contact target |
file_download | Following a link to an allowed file extension |
social_share | A share widget reports success through the xpac-analytics-share document event |
video_start, video_pause, video_progress, video_complete | HTML video playback |
audio_start, audio_pause, audio_progress, audio_complete | HTML audio playback |
Media progress records 10%, 25%, 50%, and 75% checkpoints. These are HTML media events; embedded third-party players need their own integration. Milestones require active playback: seeking before playback starts or while paused does not create progress events. Finishing playback or replacing the source resets milestones for the next play.
Events include page URL/title/referrer, language, screen size and elapsed engagement
time while the page is visible. Page context also includes page type, post type,
post ID and template. This site's own record keeps every event in the catalogue: a
page_view becomes a view, every other event becomes an event on the page it happened
on, and the engagement time each one carries is added to that page's time on page.
user_engagement carries time and nothing else, so it adds to the view and is not
counted as an event. Each destination add-on chooses separately which events leave the
site. The public server route validates event names, scalar properties, source host,
timestamps and identifiers. It rate-limits requests and deduplicates event IDs.
An event is stored with its declared properties only, scrubbed the same way search terms are: a URL keeps its scheme, host and path and loses its query string, and anything that looks like an email address, a phone number or a token is masked before it is written. The value Overview lists under an event is the one property that says what it happened to — the file for a download, the link for a click, the title for a video, the form or popup ID, the contact method, the scroll percentage — or, for a custom event, its first declared property.
Engagement settings
Settings → Engagement controls capture:
| Setting | Behavior |
|---|---|
| Scroll depth percentages | One scroll_depth event per value; defaults to 90 |
| Search query parameters | URL parameters that mean a search; defaults to s |
| Download extensions | Links to files with these extensions count as downloads; blank uses the built-in list of documents, archives and media |
| Additional internal domains | Links to these domains are not counted as outbound; www and apex forms are paired |
| Count page views after in-page navigation | Opt-in page views after history URL changes; hash-only changes are ignored |
| Send time on page when a visitor leaves | Opt-in user_engagement when the page becomes hidden, with page exit as a fallback |
| Track XPAC form interactions | Opt-in form_start, form_submit, and form_error with a form ID |
| Track popup opens and closes | Opt-in popup_view and popup_close with popup/rule IDs |
| Group content by | No grouping, Post type, or Category with the lowest ID |
| Custom click events | Declare event names and permitted properties for marked links or buttons |
The four optional tracking switches default to off. Enabling a capture also adds its events to the catalogue, and a destination add-on's settings page picks them up after you save. Switching mobile apps can hide a page without unloading it, so hiding also flushes visible engagement time. A later page exit does not send that same interval twice.
Form capture never includes answer values. It excludes forms containing password or
authentication-related input controls and forms marked data-xpac-analytics-ignore.
Accepted responses marked as UserAuth are not recorded as generic form submissions.
For a custom click, declare content_open and the property content_id, then mark
the relevant element:
<button data-xpac-event="content_open"
data-xpac-event-properties='{"content_id":42}'>Open article</button>Only the declared scalar properties are captured. Each destination add-on decides what
its own product calls the event, so a custom event can reach one destination under a
different name. Use identifiers rather than personal content in these properties.
Names that collide with settings are unavailable as custom events: id and enabled
always, plus the names each installed destination add-on reserves for its own
settings, such as secret, user_data, access_token and test_event_code. See the
developer integration guide
for limits and DOM event contracts.
General settings
Settings → General holds the settings that shape this site's own record:
| Setting | Behavior |
|---|---|
| Exclude user roles from tracking | Visits from signed-in users with these roles are not counted |
| Keep this site's own traffic history for | Keep forever (default), 30, 60, 90 or 180 days, or 1 to 4 years. Applies to this site's own reports only. Each daily cleanup deletes the visits, page views and events older than the window, the daily visitor records last seen before it and the daily totals of the days before it, then the pages, referring sites and event values nothing kept refers to any more. Goals, chart notes, devices, browsers and campaign names stay. Deleted history cannot be recovered; a large backlog takes a few daily runs to clear |
| Delete traffic history when this plugin is uninstalled | Off by default: the report tables stay in the database after an uninstall and are picked up again on reinstall. On, uninstalling deletes them for good, with the goals and chart notes |
| Note plugin updates, settings changes and retention prunes on the chart | On by default. Off, only the notes people add appear on the Overview chart |
Consent and privacy
Settings → Privacy holds the consent controls:
| Setting | Behavior |
|---|---|
| Consent source | Off (default): consent is not asked for. WordPress Consent API reads your consent plugin. Your own JavaScript waits until your code calls window.xpADL.setConsent(...) |
| Default consent: Statistics, Default consent: Marketing | Denied by default; used until the visitor decides |
| Require statistics consent for this site's own reports | Off by default. This site's own record sets no cookie and stores no IP address; turn this on if your legal advice says audience measurement still needs consent. On, visitors who decline statistics consent are left out of this site's own reports |
| Global Privacy Control | Deny marketing (default), Deny statistics and marketing, or Ignore this signal. Denying statistics also leaves the visit out of this site's own reports |
| Identify signed-in users after consent | Off by default. On, destination add-ons can tie events from signed-in visitors to their WordPress account once they consent. This site's own reports never record who a visitor is |
Statistics gates this site's own reports when Require statistics consent for this site's own reports is on. Each destination add-on declares which category it uses.
Analytics supplies the integration, not a consent banner. With Your own JavaScript, have your consent manager call this after the Analytics runtime has initialized:
window.xpADL.setConsent({ statistics: true, marketing: false });Pass false to withdraw a category. With WordPress Consent API, Analytics reads
wp_has_consent and listens for consent-change events. If the API is unavailable,
the configured defaults remain in effect. Analytics itself talks to no third-party
consent API: each destination add-on hears the decision and applies it to its own
tag, pixel or SDK.
Denied events are skipped, not replayed when approval arrives. Withdrawing consent also invalidates pending events for the affected category; granting it again permits newly captured events without reviving the old queue.
The suggested text in WordPress's privacy-policy helper is a starting point; describe the destinations, retention and consent choices actually enabled on your site.
Purge full-page and asset caches after changing capture or privacy settings so visitors receive the new browser configuration.
Delivery
Tools lists this site's own record beside each installed destination and keeps a redacted recent failure per destination for up to a day, sampled at most once per minute. The sample contains status and time rather than visitor payloads or credentials. Use Dismiss failure after reviewing it, and Send test event to check a destination's settings.
A 429 response pauses browser delivery for the indicated wait. Behind Cloudflare the
visitor's address is read from Cloudflare's header automatically, because the request
arrives from Cloudflare's own network. A different CDN or load balancer adds its
ranges with the xpac_analytics_trusted_proxies filter described in the
developer guide; otherwise its shared
address puts every visitor in one rate-limit bucket and one visitor identity.
Site Health
Tools → Site Health runs six Analytics checks:
- Analytics recording: the report tables exist and this site is recording its own traffic.
- Analytics report storage: storage is prepared ahead. Fewer than 30 days left is flagged, and any wp-admin visit extends it.
- Analytics delivery failures: no destination reported a failure in the last 24 hours.
- Analytics request cleanup: leftover request records are removed automatically.
- Analytics consent compatibility: every installed destination add-on says which consent it needs.
- Analytics public endpoint: the public endpoint answers. The probe sends an invalid event and expects a validation error; it never creates an event anywhere.
A green check does not prove that a destination has included events in its reports. The Info tab lists recording state, schema version, retention, storage prepared ahead, the add-on contract version, active add-ons, consent source, object cache, cleanup and recent failures, without credentials or visitor data.
WP-CLI
wp xpac analytics status prints JSON with the contract version, the consent source,
a store block (recording, tables, schema version, retention, whether anything has
been recorded, and days of storage prepared ahead) and one entry per installed
destination. test-event <destination> sends one synthetic event to a destination,
prune removes expired request records, upgrade builds any report indexes an update
left for later (see below). See the
command reference for output.
On a site with more than 200,000 stored visits, views or events, the 1.3.0 update adds
its report indexes in the background, one table at a time, rather than inside a page
load. The health line at the foot of Overview reads Building indexes until they are
done; filtered views work meanwhile, only slower. wp xpac analytics upgrade finishes
the job at once on a host without a working WP-Cron.
Send events to a third party
Each destination is its own plugin: Google,
Meta or Mixpanel.
Analytics carries none of it: no tag, pixel or SDK, no destination keys, no
destination messages and no destination controls. Each add-on requires the
xpac-analytics plugin, brings its own browser tag and server route, adds its own
page under Settings with an Event delivery table (browser and server switches
per event, search, column checkboxes, and Restore event defaults for that page
only) and its own Send test event tool on the Tools screen, and declares which
consent category it uses. Delivery runs after the page response with no queue or
retry; a successful /track response confirms intake, not delivery. An active
destination means its configuration and route are available. It does not prove that
the destination has included an event in its reports.
Uninstalling Analytics deletes only its own General, Privacy and Engagement settings; each add-on removes its own settings when it is uninstalled.