All posts

How page caching breaks WordPress analytics

A common sequence: install a caching plugin on a Monday, look at analytics on a Tuesday, find traffic down by most of itself. Nothing happened to the traffic. The counting stopped.

Why a cache stops the counting

Ordinarily a WordPress page view runs PHP. WordPress boots, plugins load, the page is assembled, and somewhere in that process an analytics plugin gets its chance to record a visit.

A page cache exists to skip all of that. The first visitor gets the full PHP render and the result is stored as flat HTML. Everyone after that is served the stored file. That is the entire point, and it is why caching makes sites fast.

But the analytics plugin lived in the part that got skipped. If it counted during the PHP render, it now counts one visit in however many the cache serves.

The more aggressive the cache, the worse the undercount. A page cached for an hour on a busy post might record one view in four hundred.

It gets worse with a CDN

A caching plugin skips PHP but the request still reaches your server, which means there is at least something to work with.

A CDN sitting in front of the site does not even do that. The request is answered at an edge location that may be on another continent, and your origin server never hears about it. There is nothing on your side to count, because nothing arrived.

This catches people who have a caching plugin configured carefully and then put Cloudflare or similar in front of it in caching mode, and cannot work out why the careful configuration stopped helping.

The three ways round it

One: exclude analytics from the cache. Most caching plugins let you exclude specific pages or query strings. It works, and it costs you the thing you installed the cache for. Excluding your most popular pages so they can be counted is close to self-defeating.

Two: count with JavaScript. A script in the page fires a request after load, and because it runs in the browser it does not care whether the HTML came from a cache. This is what most third-party analytics does, and it is why caching does not usually break Google Analytics.

The cost is everything a script costs: anyone with a content blocker is invisible, anyone with JavaScript off is invisible, and you are now loading something extra on every page. If the script is on a third-party domain it is on every blocklist there is.

Three: count outside the cached HTML. Record the visit in a request that is separate from the page render, so the cached page can be served untouched and the count still happens. This is the approach Honest Analytics takes: pageviews are recorded outside the cached HTML, so a cached page still counts, and a small first-party script confirms the detail rather than being the only source. A visitor blocking scripts is still counted, with less detail.

No approach is free. The first costs speed, the second costs coverage, the third costs a request that a pure static cache would not otherwise make.

How to tell if this is happening to you

The signature is distinctive:

  • Traffic drops sharply and permanently on the day caching changed, rather than tapering.
  • Logged-in traffic looks normal. Caching plugins usually bypass the cache for logged-in users, so your own visits keep being counted, which is why the site looks fine when you check it.
  • Your host’s own bandwidth or request graphs disagree with your analytics.
  • Popular pages are hit hardest, because popular pages are the ones being served from cache most often.

That last one is the giveaway, and it is the most damaging version of the problem. The undercount is not uniform. It is worst exactly where the traffic is, so your best-performing content looks mediocre and your quiet pages look comparatively healthy. Every decision you make from that data is skewed.

Server logs are not the answer

The obvious thought is to count from server logs instead, since the server sees everything.

It sees too much. Raw logs are dominated by crawlers, uptime monitors, security scanners and preload requests, and separating those from people is most of the work in analytics. Logs also stop at the CDN edge, so the CDN problem above remains. They are useful for debugging and poor as a traffic report.

Checking your setup

If you run a cache, verify the counting rather than assuming it:

  1. Open the site in a private window, as a logged-out visitor, and load a page you know is cached.
  2. Check whether the visit appears.
  3. Repeat from a different device or network.

If logged-out cached views do not appear, you have the problem, whatever the documentation promised.

The caching notes cover the specific setups that need a tweak, including the CDN case. If you are choosing an analytics tool and you run a cache, this is the first question to ask, before any question about features.