Skip to content
Operations

Analytics

See what people read on the published site. Page views by default, with visitors, sources and devices as an option.

On this page

The Analytics plugin (letterncms/analytics) counts visits to the published site on your own server. Open Analytics in the sidebar for the numbers; the Analytics widget on the home screen shows the last 30 days at a glance.

#What it counts

Under Settings → Analytics, What to count has two settings. Page views only is the default.

Page views only (counts)Visitors, sources and devices (visitors)
On the pageOne 1×1 image, no scriptA few hundred bytes of script
SentThe page's path, written into the page at buildPath, referrer, campaign name, window width
Read from the browserNothingReferrer, window width, Do Not Track
KeptViews per day and pathAlso a daily hash per visitor, source, device, browser and operating system
IP address and browser stringRead in memory to skip bots, then goneHashed with a salt that is replaced daily and thrown away
ReportsPage views, top pages, languagesAll of them

In both, people who ask not to be tracked (Do Not Track or Global Privacy Control) and bots are skipped, nothing is counted in previews or the block workspace, and people signed in to the admin aren't counted. That last one uses a cms_staff cookie set when someone with admin access signs in. It's HTTP-only and no page or script reads it. A site's members are counted like any visitor.

Visitors mode stores a pseudonymous hash and reads data from the browser. Whether that needs consent depends on your visitors' countries, so check the rules for them. By default it waits for a signal from your consent tool: window.cmsConsent = { analytics: true } set before the page's script runs, or a cms:consent event on document with detail: { analytics: true }. Turn off Wait for consent if you've decided you don't need it. Web server logs are separate from all of this, and are your host's matter.

Going back to page views only deletes the visitor data and its salt. Views per page stay. Switching to visitors starts collecting from that day.

#What you see

NumberMeans
Page viewsEvery page opened.
VisitorsDifferent people per day, added up over the period. Visitors mode.
Views per visitPage views divided by visitors. Visitors mode.
Bounce rateThe share of visitors who opened one page and left. Visitors mode.

Each number is compared with the period before (7, 30 or 90 days), and you can chart it per day. Below are the top pages and the views per language. In visitors mode you also get the pages visits start on, sources (the site that linked to you, or the campaign in a link's utm_source or ref parameter, "Direct" when there is none), devices, browsers and operating systems, and On the site now, which counts visitors active in the last five minutes.

#Settings

Settings → Analytics:

Setting
Count visitsOff stops counting; the numbers collected so far stay.
What to countSee above.
Wait for consentVisitors mode only.
Paths not to countPaths or patterns with *, e.g. /drafts/*.
Keep data forDays of history to keep (730 by default); 0 keeps everything.
Lettern URLOnly for a site published to another domain: the address of this Lettern, which pages report to.

#How it works

In counts mode, a page ends with <img src="/api/analytics/view?p=/pricing">. The endpoint always answers with the same 1×1 GIF and Cache-Control: no-store, counted or not, so it reveals nothing. It reads no session and counts only paths that exist, with a same-origin Sec-Fetch-Site header when the browser sends one. Its rate limit is one bucket of 600 a minute for everybody, because a key per visitor would mean storing a hash of their address. Past the limit a view isn't counted.

In visitors mode, the script posts to /api/analytics/hit after the page loads. Lettern keeps one row per visitor and day and daily totals per page; the endpoint is rate-limited per address and accepts views from the site's own pages only.

Agents and the API read the same numbers through the analytics/summary and analytics/breakdown abilities (permission analytics.view, which editors have by default). In counts mode, numbers it can't know are null, and breakdowns other than pages and languages answer {"unavailable": "mode"}. A plugin with pages of its own that aren't entries lets them be counted through the analytics.known-path filter.

For development, php cms analytics:demo fills an empty site with made-up traffic for the pages it has (--days=120, --fresh to replace what is there). It refuses to run in production.