Key Takeaways
- Most analytics dashboards are designed for the tool, not for the person asking the question. Replacing Slimstat with a purpose-built plugin meant starting from what a website owner actually needs to know.
- Edward Tufte's information design principles – particularly the data-ink ratio and the elimination of chartjunk – guided every interface decision.
- Harry Beck's London Underground map proved that stripping away geographic reality to reveal topology makes complex systems comprehensible. The same principle applies to website visitor data.
- Filtering noise is a design decision, not a technical one. Excluding bots, datacentre traffic, and non-Australian visitors before displaying results changed the dashboard from overwhelming to actionable.
- The Visitor Journeys matrix – showing every real session as a colour-coded path through the site – answers the question analytics should have always answered: what did actual humans do on my website?
The Problem with Analytics as We Know Them
I've been building websites since 1992. For most of that time, website analytics meant staring at numbers that felt important but rarely told you anything useful.
Google Analytics gives you sessions, bounce rates, page views, time on site. Enterprise tools pile on heatmaps, funnels, cohort analyses. WordPress plugins like Slimstat try to bring some of that into your dashboard. They all share the same fundamental problem: they show you data without showing you what happened.
When a client asks me "is my website working?", they don't want a bounce rate. They want to know whether real people – potential customers in their area – are finding their site, reading their content, and taking action. That's a completely different question, and it requires a completely different interface to answer it.
Why Slimstat Had to Go
I'd been using Slimstat Analytics across my client sites for years. It was one of the better self-hosted WordPress analytics plugins – privacy-friendly, no third-party data sharing, reasonable feature set. But it had accumulated serious problems.
The database tables grew without bound. On busy sites, the pageview table could run to millions of rows with no automatic pruning. Every dashboard load ran expensive aggregate queries against those tables, slowing the admin substantially. The plugin tracked everything – bots, crawlers, vulnerability scanners, SEO tools – with no meaningful filtering. The raw volume of noise made the signal invisible.
The tipping point came when I examined the data closely on a client site that was running Google Ads. The Slimstat data showed thousands of sessions, but when I filtered by geographic location, 95% of the traffic was from outside Australia. This was a Melbourne trades business – a plumber isn't going to fly in from Mumbai. Worse, 99.5% of all visits were single-page sessions – visitors who hit one page and immediately left. The client was paying for Google Ads clicks that were overwhelmingly bots, scrapers, and irrelevant international traffic.
That data couldn't be extracted from Slimstat without manual SQL queries. The dashboard showed inflated session counts that looked encouraging on the surface. The reality underneath was that almost none of that traffic was genuine.
I didn't want a better version of Slimstat. I wanted to rethink what website analytics should look like when you design them for the person asking the question.
Starting with Tufte, Not with Technology
Before writing a single line of code, I went back to first principles. Specifically, I went back to Edward Tufte.
Tufte's The Visual Display of Quantitative Information, first published in 1983, is one of the most important books on information design ever written. He self-published it after taking out a second mortgage on his home because no publisher would take it on – a decision that turned out rather well. The book has sold over two million copies and fundamentally changed how statisticians, scientists, and designers think about presenting data.
The core principle that shaped my analytics plugin is what Tufte calls the data-ink ratio. In any statistical graphic, Tufte argues, every drop of ink should present new information. Non-data ink – decorative borders, gradient fills, 3D effects, gridlines that serve no purpose – is waste. He condenses this into a simple formula: maximise the share of ink devoted to data, and erase everything else.
Tufte coined the term chartjunk for the decorative clutter that designers add to charts – cross-hatching, heavy containers, ornamental elements, "ducks" (graphics that are all decoration and no data). His argument, supported by extensive cognitive research, is that chartjunk doesn't just waste space. It actively impedes comprehension. The reader's visual system has to filter out the noise before it can process the signal. Sound familiar? That's exactly what was happening with Slimstat's dashboard.
The second Tufte principle I leaned on was small multiples – using the same visual structure repeated across a series, each showing a different slice of the data. When viewing conditions are consistent, the eye can compare across panels effortlessly. Differences in the data become immediately visible because the frame is identical.
These principles aren't abstract aesthetics. They're cognitive science. NNGroup's research – How Users Read on the Web – has consistently shown that 79% of users scan rather than read. In a dashboard context, scanning is the only mode. Nobody reads an analytics interface line by line. They glance, orient, and look for patterns. Every pixel of chartjunk makes that harder.
The Underground Map Insight
The other design reference that shaped my thinking was Harry Beck's 1931 London Underground map.
Before Beck, the Underground's official maps were geographically accurate. They plotted stations at their real positions across London, with routes following their actual paths. The result was a cluttered mess – central stations were crammed together while outer stations were spread across vast expanses of white space. The information people actually needed (how to get from station A to station B) was buried under geographic detail they didn't care about.
Beck, an engineering draughtsman at the Underground, had a radical insight: because the railway ran mostly underground, the physical locations of stations were largely irrelevant to the traveller. Only the topology of the route mattered – which stations connect to which, and where do you change lines? He stripped away geographic accuracy entirely, used only horizontal, vertical, and 45-degree diagonal lines, and spaced stations evenly for legibility.
The result was one of the most influential information design artefacts of the twentieth century. It was voted a top-ten British design icon in 2006. And it works because Beck understood something fundamental: the right abstraction reveals the structure that matters.
Website visitor analytics have the same problem the pre-Beck Underground map had. They plot data at "geographic" resolution – exact timestamps, pixel coordinates, individual HTTP headers – when users need topological understanding. Which pages did a visitor see? In what order? Did they explore, or did they bounce? That's the topology of a website visit, and no amount of geographic detail (exact seconds on page, scroll depth percentages, viewport dimensions) helps you see it faster.
Designing by Subtraction
Armed with Tufte's data-ink principle and Beck's topological abstraction, I started the design not by listing features to build but by listing things to remove.
Remove the bots
Traditional analytics track every HTTP request and let you filter later. That's backwards. By the time bot traffic is in your database, it's already corrupting your aggregates. My plugin runs a dual-tier detection pipeline before recording anything. The first tier uses Jaybizzle's CrawlerDetect library – a maintained pattern database covering hundreds of known crawlers. The second tier checks user-agent strings against a curated keyword list for anything CrawlerDetect misses.
But user-agent strings can be faked. So the plugin also performs ASN-based datacentre detection using MaxMind's GeoLite2-ASN database. If a visitor's IP resolves to an Autonomous System belonging to AWS, Google Cloud, Azure, DigitalOcean, or any of approximately 40 other cloud infrastructure providers, they're flagged as a likely bot or automated tool. Legitimate website visitors browse from residential or workplace ISPs, not from datacentre IP ranges.
Remove the noise geographically
For Australian small businesses – which is 100% of my client base – international traffic is almost always noise. A Melbourne hairdresser doesn't need to know about visitors from São Paulo. The dashboard has a "Australia Only" toggle that filters the entire view to domestic traffic using MaxMind's GeoLite2-City geolocation database. One click collapses thousands of irrelevant data points and reveals the traffic that actually matters.
Remove the vanity metrics
Bounce rate, average session duration, pages per session – these aggregate statistics feel authoritative but hide more than they reveal. A "3-minute average session duration" could mean some visitors spent 10 minutes reading your services page while hundreds of bots triggered one-second visits. The average is meaningless. I chose to show individual sessions instead of aggregates, so you see each visitor's actual journey rather than a statistical abstraction of it.
Remove the clutter from the data display
Following Tufte's data-ink ratio ruthlessly: no gradient backgrounds. No 3D effects. No decorative chart elements. No heavy borders or shadows on data cards. The overview section is eight flat summary cards – sessions, unique visitors, pageviews, pages per session, mobile percentage, bots blocked, IPs blocked, and a server load indicator. Each card shows a single number in a large, clear typeface. No colour-coding, no trend arrows, no sparklines that add visual noise without adding comprehension.
The Visitor Journeys Matrix
This is the heart of the plugin, and the piece I'm most proud of.
The Visitor Journeys matrix is a two-dimensional grid. Each row represents one real visitor session. Each column represents one page on the website. The cells show visit order – numbered pips indicating which page was visited first, second, third, and so on. Cell colour encodes position in the journey: green for entry pages, amber for mid-journey pages, and red for exit pages.
That's it. No charts. No graphs. No aggregation. Every real session, laid out as a path through the site.
This is Beck's insight applied to web analytics. The "geographic" detail – exact timestamps, scroll positions, mouse movements – is stripped away. What remains is the topology: where did each visitor enter, what path did they take, and where did they leave? The matrix makes patterns visible instantly:
- A column that's almost entirely green tells you that page is a primary entry point – probably your homepage or a well-ranked landing page.
- A column that's mostly red reveals an exit page. If that's your contact page, excellent – people are completing their journey. If it's your services page, something is sending them away.
- Rows with many coloured cells show engaged visitors browsing deeply. Rows with a single green cell show single-page bounces.
- Recurring patterns of green-amber-red across multiple rows reveal your site's natural navigation flow – the paths people actually follow, which may differ dramatically from the paths you designed.
This is what Tufte means by data density – encoding maximum information per unit of display area. Every cell in the matrix carries meaning. There is no chartjunk. The colour coding is functional, using just three hues with clear semantic mapping. The eye can scan fifty sessions in seconds and spot anomalies that would be invisible in a line graph or pie chart.
The sticky metadata column
The leftmost column stays pinned as you scroll horizontally across pages. It shows each session's timestamp, country flag, city, session duration, and device icons. Hovering reveals the full detail – browser, operating system, referrer URL. This is Tufte's principle of layered information: overview first, details on demand.
Why not charts?
I deliberately avoided aggregated charts for the primary view. A line graph of "sessions per day" hides the composition of those sessions. A pie chart of "traffic by country" obscures the individual journeys. These chart types abstract away the very thing you need to see – what individual visitors are doing.
The single chart in the dashboard is a traffic trends line that shows sessions over time, and it exists purely to answer "is traffic going up or down?" – a macro-level question that genuinely benefits from a time-series visualisation. For everything else, the raw data in the matrix is more informative than any aggregate could be.
Behavioural Threat Detection
The bot detection I described earlier handles known crawlers and datacentre traffic. But what about bad actors who look human? Vulnerability scanners probing for wp-login.php? Credential-stuffing tools using residential proxies? SEO scraping bots with realistic user agents?
The plugin implements a cumulative threat scoring system. Every visitor starts at zero. Suspicious behaviours add points:
- URL probing – requesting known attack paths like
/wp-admin/,/xmlrpc.php, or common plugin vulnerability URLs scores 10–40 points depending on severity. - Datacentre IP detection via ASN lookup scores 40 points.
- Rate limiting – more than 10 beacon requests within 60 seconds scores 30 points.
When a visitor's cumulative score hits 100, they're blocked. The block is recorded in the known_ips table with an expiry, and all subsequent requests from that IP are rejected at the earliest possible point in the processing pipeline – before session lookup, before GeoIP resolution, before database writes. This keeps the overhead near zero for repeat offenders.
The key design philosophy here is the same as the dashboard: filter the noise before it reaches the signal. By removing malicious traffic at the recording stage rather than the display stage, every number on the dashboard is trustworthy. Sessions means real sessions. Visitors means real visitors. The data has integrity because the pipeline has integrity.
The Beacon Architecture
Traditional WordPress analytics plugins hook into the PHP request lifecycle – they record a pageview when WordPress serves the page. This breaks with page caching. If a cached page is served by Nginx or a CDN without hitting PHP at all, the pageview is never recorded. For performance-optimised production sites, that means losing most of your data.
My plugin uses a JavaScript beacon architecture. A small inline script – injected at the very end of the page load – sends an asynchronous POST request to a generic endpoint (dx.php). The endpoint name is deliberately nondescript to avoid ad-blocker pattern matching. The POST keys are single letters (u for URL, t for title, r for referrer) for the same reason.
The beacon fires after the page has rendered, so it never blocks the user's experience. When the visitor navigates away, a departure beacon fires via the browser's sendBeacon() API – a fire-and-forget request that survives tab closure. This captures the actual time spent on each page without requiring JavaScript timers or heartbeat polling.
This approach means the analytics work regardless of caching configuration. Whether the page is served from WordPress, from a Redis object cache, from Nginx FastCGI cache, or from a CDN edge node – the JavaScript beacon fires either way. It's the same decoupled architecture that Google Analytics uses, applied at the self-hosted level.
What I Learned from a Client's Wasted Ad Spend
The Google Ads audit I mentioned earlier deserves more detail, because it crystallised why this approach matters.
A client was spending several hundred dollars a month on Google Ads for their trades business. Slimstat showed healthy-looking traffic numbers – hundreds of sessions per week. The client was happy with the "exposure". Their previous web agency had set up the ads and was providing reports that showed impressions and clicks going up.
When I installed the new analytics plugin with geographic filtering and bot detection, the picture collapsed. The genuine, Australian, human traffic was a tiny fraction of what the old numbers suggested. Most of the "clicks" were from IP ranges belonging to cloud hosting providers. Many of the "visitors" had user agents matching known scraping tools. The residential traffic that did come through was overwhelmingly single-page sessions from outside the service area – people (or bots) who hit the landing page and left immediately.
The monthly ad spend was essentially subsidising bot clicks and irrelevant international traffic. By switching to analytics that filtered this noise before displaying results, the client could see for the first time what their ad spend was actually buying. The answer was: almost nothing. That's a conversation you can only have when the data is trustworthy.
Technical Design Decisions
A few implementation choices are worth noting for developers and WordPress professionals.
Session management without cookies
The plugin creates sessions based on IP hash, user agent, and a 30-minute inactivity window. No cookies are set, no local storage is used, no fingerprinting scripts run. This makes the plugin GDPR-compliant without a cookie consent banner – a significant advantage for small business sites where cookie consent pop-ups create friction and annoy visitors.
IP addresses are stored as SHA-256 hashes. The raw IP is never written to disk. This provides enough uniqueness for session correlation while making it technically impossible to reverse-engineer the visitor's actual IP address from the stored data.
Five tables, not fifty
The entire data model uses five database tables: sessions (one row per visit), pageviews (one row per page hit within a session), known_ips (threat scores and block status), beacon_log (processing performance metrics), and ip_threats (detailed threat event logging). Slimstat used a substantially more complex schema. Keeping the table count minimal means queries are fast, maintenance is simple, and the plugin's database footprint stays manageable.
Automatic data retention
Old data is pruned automatically via a configurable retention policy. There's no manual cleanup required and no unbounded table growth. This is the opposite of Slimstat's approach, where tables grew until they caused performance problems serious enough to notice.
PHP 7.4 through 8.5 compatibility
The plugin runs on PHP 7.4 through 8.5 – critical for shared hosting environments where the PHP version isn't always up to date. A polyfill file provides str_contains(), str_starts_with(), and str_ends_with() for older PHP versions. Every code change is validated against PHPCompatibility with --runtime-set testVersion 7.4-8.5 before deployment.
Designing for the Person Asking the Question
The thread running through all of these decisions is the same one that's run through my entire career as a usability architect: understand who's asking, what they need to know, and strip away everything else.
Tufte's data-ink ratio isn't just about making charts prettier. It's about respecting the viewer's cognitive capacity. Beck's Underground map isn't just a clever simplification. It's proof that the right abstraction reveals truths that "accurate" representations hide.
Website analytics has been stuck in a mode where more data equals better insight. The opposite is true. Less data – filtered, curated, presented with rigorous information design – reveals more. When you remove the bots, remove the irrelevant geographies, remove the vanity metrics, and remove the visual clutter, what remains is a clear picture of what real people are doing on a real website. That's all a business owner ever wanted to know.
If you want to see what your website traffic actually looks like without the noise, get in touch. I deploy this analytics plugin across all the websites I build and manage, and the clarity it provides has changed how I approach every client conversation about website performance.
