Odometer for Craft CMS

Configuration

Everything here has a working default. A fresh install needs none of it.

Settings live at Settings → Plugins → Odometer, and in project config like any other plugin's.

Counting

How views are counted

Automatically (default)Counted while Craft renders the page. Two statements, no reads.
With a beaconThe page carries a signed token; the browser fetches one tiny URL.
Only where askedNothing is counted except where a template or some PHP says so.
Not at allNothing is counted. Everything already counted is left alone.

Automatic is right for most sites and wrong for exactly one kind: a site behind a full-page cache, where Craft renders once and every reader after the first is invisible.

Beacon type

An image is a 1×1 GIF. It counts readers with JavaScript switched off, and it is served from your own domain rather than an analytics host, so most blockers leave it alone.

A script does not count a page the browser merely prefetched, preloaded or prerendered but nobody looked at — which an image cannot tell apart from a reader. It carries its URL in a data attribute rather than inline code, so it needs no unsafe-inline in a content security policy.

Token lifetime

Minutes a beacon token stays valid. The default, 0, means it never expires, and that is deliberate: in beacon mode the token lives inside the page's HTML, and on a cached site that HTML may be served for weeks. An expiring token there is a counter that quietly stops working, days after anybody was watching.

The token permits exactly one thing — adding to one element's count — and every other rule (robots, addresses, repeat visits) still applies to the request carrying it.

Repeat views

How many minutes before the same visitor reading the same page counts again. Default 30.

0 counts every single request, refreshes included, which is occasionally what you want (a downloads counter) and usually not (an article).

The test hashes the address and user agent with the site's security key and keeps the hash in Craft's cache for exactly that long. Nothing is written to the database and there is no cookie.

What gets counted

Sections — every one, only a list, or every one except a list.

Other element types — categories, products, and anything else with a URL. Sections say nothing about these, so they get their own switch rather than an accidental answer.

Drafts, revisions, previews and control-panel requests are never counted, and there is no setting for that.

Who gets counted

Ignore robots (on) with a list of about sixty user-agent fragments, matched case-insensitively anywhere in the string. A user agent is a claim rather than a fact, so this removes most of the noise and cannot remove all of it. Add your own; an empty user agent is always treated as a robot, because every browser sends one.

Ignored addresses — exact addresses (203.0.113.7), dotted prefixes (198.51.100.) or CIDR ranges (203.0.113.0/24, 2001:db8::/32). The office, the uptime monitor, the staging box.

Signed-in users — count everybody, ignore admins (default), or ignore every signed-in user.

Anything other than "count everybody" means asking Craft who the visitor is, and that opens a session — which means a Set-Cookie on the response, which means a full-page cache that never stores the page again. Odometer only asks once it can already see a session cookie on the request, so an anonymous reader never pays for this setting. If you run a page cache and want to be certain nothing ever touches the session, set this to Count everybody.

Honour Do Not Track (on) — skips DNT: 1 and Sec-GPC: 1. Odometer stores nothing about anybody either way, so this is a courtesy rather than a compliance control.

History

Keep day-by-day history for — 730 days by default. 0 keeps it forever.

Retention prunes day buckets and never touches lifetime totals. Turning retention down costs you the ability to ask about a window that far back; it never costs anyone their view count.

Pruning runs with Craft's own garbage collection, and on demand from the report screen or php craft odometer/views/prune.

Trending window (7 days) and trending half-life (3 days) — how far back trending looks, and how quickly a view stops mattering. A half-life of 3 means a view from six days ago counts a quarter as much as one from today. 0 turns decay off, which makes trending "most views in the window" — a fine answer, and a slower-moving one.

Control panel

Show a panel in the entry sidebar (on) — the lifetime number, the last seven days, thirty days of history, and a link to the full history. No field layout to edit.

Offer a Views column on element indexes (on) — a sortable Views column wherever elements can have URLs. Editors still have to add it to a source's columns; this only puts it on the menu.

Counters

Default counter — leave empty unless you know you want more than one counter per element. Changing it orphans every count already recorded under the old name.

To use several, pass a key to any method:

{% do craft.odometer.record(entry, { key: 'downloads' }) %}
{{ craft.odometer.views(entry, 'all', { key: 'downloads' }) }}

Permissions

  • View reports — the Odometer section, the sidebar panel, the widget, the field.
  • Reset and prune view counts (nested) — the buttons that delete things.