Three questions come up again and again from people staring at a Matomo dashboard. "If the same person comes back in a year, are they still one unique visitor, or does the clock reset?" "What's the actual difference between unique pageviews and visits — sometimes they look like they should be identical?" And "I'm getting duplicate entries for the same person, how do I make Matomo merge them?"
They look like three separate problems. They're the same one: how does Matomo decide who's who, and when does it start counting again? Every visitor number you see (unique visitors, returning visitors, unique pageviews) comes out of that one decision. Get the recognition model and the rest stops being mysterious. Here's how it works in Matomo 5.
Matomo recognizes a visitor by User ID first, then the _pk_id cookie, then a short-lived config_id hash that resets every day. Unique Visitors is counted per period you pick, so daily counts never add up to a weekly or monthly total; read the longer period directly instead of summing them. Server-side tracking is the other trap: it can't see the cookie, falls back to config_id, and splits one person into a dozen visitors. Send a stable 16-character _id yourself and that goes away.
How Matomo recognises a visitor
When a tracking request arrives, Matomo works out which existing visitor it belongs to. The real matcher has special cases for cookies, a manually set cid, the User ID, and the trust_visitors_cookies setting, so it isn't a single rigid ranking. But for reading your dashboard, the practical model is a fallback chain:
- User ID: if you've set a User ID (a logged-in account ID, a CRM ID, anything stable you own), Matomo treats it as the same person across devices and browsers, because it's a value you assign rather than something it has to infer. It's the strongest signal you can hand it.
- Visitor ID (the cookie): the JavaScript tracker stores a 16-character visitor ID in the first-party
_pk_idcookie. As long as that cookie survives, the same browser is recognised as the same visitor on every return trip. - config_id hash: with no User ID and no cookie, Matomo falls back to an anonymised
config_idbuilt from operating system, browser, plugins, IP, and language. Matomo deliberately keeps this short-lived rather than running it as a persistent fingerprint.
That short life is the whole point, and it explains a lot of "why are my returning visitors so low?" confusion. The random seed behind config_id gets thrown away and regenerated every 24 hours, so the same device produces a different hash each day. And when Matomo matches on it, it only looks back window_look_back_for_visitor seconds, 30 minutes by default. So without a cookie or a User ID, a returning visitor is basically invisible across days. Recognising someone from one day to the next is a cookie job, not a config_id job, and that's by design. It's part of what makes Matomo's cookieless mode workable for consent-exempt analytics in some jurisdictions, when it's configured for that — check your local law before relying on it.
One more boundary: recognition is scoped per site. Visitor IDs and config_id hashes don't carry across separate sites or measurables, so the same person on two of your properties counts as two visitors. If you want several domains to count as one journey, configure them as alias URLs under a single Website with cross-domain linking; don't assume a shared User ID merges the aggregate counts across separate site IDs without testing it.
How long someone stays the "same" visitor
This is the "unique visitor length" question, and the answer is the cookie lifetime. The JavaScript _pk_id visitor cookie lasts 13 months by default, and you set it with setVisitorCookieTimeout():
// Default is 13 months (33696000 seconds). Shorten or extend as needed.
_paq.push(['setVisitorCookieTimeout', 33696000]);So if the same browser returns within 13 months and the cookie hasn't been cleared, Matomo matches it to the same visitor ID and counts it under "Returning visits", "Days since last visit", and so on. (The companion _pk_ses session cookie is separate and expires after 30 minutes, which is the visit timer, not the identity timer.)
Worth clearing up one thing: a 13-month cookie doesn't mean everyone collapses into one giant lifetime "unique visitor" count. The cookie is about recognition. The metric is scoped separately, which is the next piece.
Unique Visitors is always per period
The Unique Visitors metric is always scoped to the period you're looking at: unique per day, unique per week, unique per month. Someone who visits Monday, Wednesday, and Friday is three daily uniques but one weekly unique, as long as the cookie or User ID survives so Matomo recognises them across those days. That one fact explains the most common visitor-counting mistake.
You can't add up daily unique visitors to get a weekly or monthly total. Summing them double-counts everyone who came back more than once. Read the week or month period directly instead.
There's a performance wrinkle too. Self-hosted Matomo processes Unique Visitors for day, week, and month by default. Matomo Cloud defaults to day and week; monthly can need a support request depending on your plan, and year or custom-range uniques aren't a self-service flag there. On self-hosted, yearly reports and custom date ranges are off by default, because computing uniques across a long span is expensive. Turn them on in your config file:
[General]
enable_processing_unique_visitors_year = 1
enable_processing_unique_visitors_range = 1After you change these, you have to re-process the relevant historical archives, and on a busy site it costs real CPU. Turn on only the periods you actually report on.
What counts as a "visit"
A visit (a session) starts on the first action and stays open until 30 minutes of inactivity (visit_standard_length = 1800 seconds). An action after that gap opens a new visit: same person, second visit. A visit also splits at midnight in the site's timezone. A campaign change starts a new visit by default (you can switch that off with create_new_visit_when_campaign_changes), while a website-referrer change does not, unless you turn it on. So "visits" counts sessions, and one person can rack up plenty of them.
Unique pageviews vs visits
This is the comparison that trips people up, and it mixes two different things. Pageviews and Unique Pageviews are page-report metrics; Visits is a site-level metric.
For a single page, the two page metrics differ only on reloads:
- Pageviews is every load of the page.
- Unique Pageviews counts one per visit that loaded the page, no matter how many times it was loaded in that visit.
So say four visits land on /pricing and load it 1, 2, 3, and 3 times:
| Visit | Loads of /pricing |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 3 |
| Pageviews | 9 (every load) |
| Unique pageviews | 4 (one per visit that loaded the page) |
Reload the same page inside one visit and pageviews climb while unique pageviews don't. They only match when nobody reloads.
Visits is a different number altogether: the count of sessions across the whole site. Unique Pageviews for one page is a subset of that, the visits that happened to include the page. They line up only when every site visit touched that page, which is rare; usually total site Visits is the larger number.
Why server-side tracking splits one person into many
Now the duplicate-visitor problem. It almost always shows up the same way. You're tracking server-side, say a Cloudflare Worker logging PDF downloads through the HTTP Tracking API, and one person fans out into a dozen separate visitor entries.
The cause comes straight from the recognition order above. A server-to-server request doesn't carry the browser's _pk_id cookie, and unless your worker reads it off the incoming request and forwards an equivalent ID, Matomo falls back to config_id, which resets every 24 hours and only looks back 30 minutes. Every gap longer than half an hour mints a fresh "visitor". The fix is to stop leaning on config_id and send a stable visitor ID yourself.
The download parameter is what logs the request as a file download. Sending only url= records it as a pageview, which is the quiet reason a lot of server-side "download tracking" never shows up under Downloads.
# Log a PDF download for a known visitor.
# _id is a 16-char hex that stays the same for that person on every request.
curl "https://matomo.example.com/matomo.php" \
--data-urlencode "idsite=1" \
--data-urlencode "rec=1" \
--data-urlencode "_id=a1b2c3d4e5f60718" \
--data-urlencode "url=https://example.com/whitepaper" \
--data-urlencode "download=https://example.com/whitepaper.pdf" \
--data-urlencode "cip=203.0.113.9" \
--data-urlencode "token_auth=YOUR_TOKEN"Two rules make this work. First, the _id has to be a 16-character hexadecimal string that stays identical for the same person on every request. The durable way to get one is to issue it yourself: set a first-party cookie when you serve the page and reuse its value, or for logged-in users derive it from the account ID with a salted hash (or just send their uid). Don't use a per-request value like cf-ray, and skip the tempting shortcut of hashing IP plus user-agent: it merges everyone behind a shared IP, falls apart when mobile addresses rotate, and quietly turns a cookieless setup into a persistent pseudonymous identifier you'd then have to justify. Second, set cip to the real visitor IP so geolocation reflects the visitor instead of your worker's datacenter; that parameter needs token_auth.
One more gotcha the PDF case exposes. If your worker logs show duplicate edge executions or prefetches firing near-identical requests seconds apart, dedupe at the worker (a short-lived cache keyed by _id plus URL) before you ever call Matomo, rather than hoping it merges them afterwards. And once requests do land in one visit, repeated downloads still show up as separate download actions by design, so compare "unique downloads" against "downloads" in the report instead of expecting Matomo to fold them together.
What we'd actually do
If you care about recognising real people across days, the order of leverage is simple. Set a User ID for anyone who logs in. Keep the visitor cookie on. Lean on config_id only for the anonymous tail, and don't expect it to remember anyone past the same afternoon. For server-side tracking, own the _id. A stable identifier you control beats any amount of config tuning, and it's the one change that turns "twelve duplicate visitors" back into one. And when a number looks wrong, check which period it's scoped to before you blame the tracker. Nine times out of ten you're adding up daily uniques that were never meant to be added.
Martez connects Matomo with Meta Ads and Google Ads so ROAS, CLV, and attribution sit next to your web analytics instead of in a separate spreadsheet. It's in private beta. Join the waitlist if that's relevant.
Matomo isn't counting wrong. It's counting exactly what you told it to recognise.