Overview
Matomo (formerly Piwik) is a leading open-source web analytics platform that provides full data ownership and privacy compliance. Unlike hosted analytics solutions, Matomo gives you complete control over your data, making it ideal for organizations with strict privacy requirements or those operating in regulated industries.
This comprehensive guide covers all installation methods for Matomo across web, mobile, and server environments, including self-hosted and cloud deployments.
Key Features
- Open-source and self-hosted: Full control over data collection and storage
- Privacy-first analytics: GDPR, CCPA, and HIPAA compliance built-in
- Cookieless tracking: Option to track without cookies for enhanced privacy
- Comprehensive reporting: Real-time visitors, ecommerce, goals, and custom reports
- Tag Manager: Built-in tag management system for advanced implementations
- Heatmaps and session recording: Visual behavior analysis (premium feature)
Deployment Options
Matomo offers two primary deployment models:
- Matomo Cloud: Fully managed hosting by Matomo team
- Matomo On-Premise: Self-hosted on your infrastructure
Both options share the same tracking code and features, with differences in hosting, maintenance, and pricing.
Deployment Considerations
Before installing Matomo, evaluate:
- Hosting model: Cloud vs. self-hosted based on compliance and technical requirements
- Cookie strategy: First-party cookies, cookieless tracking, or consent-based cookies
- Environment separation: Separate site IDs for development, staging, and production
- Tag Manager usage: Direct tracking code vs. Matomo Tag Manager
- Privacy requirements: Data anonymization, IP masking, and consent management
- Cross-domain tracking: Tracking users across multiple domains or subdomains
Prerequisites
For Matomo Cloud
- Create Matomo Cloud account at matomo.org/start-free-analytics-trial
- Obtain site credentials:
- Matomo URL (e.g.,
https://your-instance.matomo.cloud) - Site ID (numeric identifier for each tracked property)
- Matomo URL (e.g.,
- Configure sites:
- Add each domain/subdomain as separate site
- Set site timezone and currency
- Enable ecommerce tracking if needed
- Configure excluded parameters and IP addresses
For Matomo On-Premise
Server Requirements:
- PHP: 7.2.5 or higher (8.0+ recommended)
- MySQL: 5.5 or higher, or MariaDB
- Web server: Apache, Nginx, or IIS
- PHP extensions: PDO, mysqli, GD, openssl, mbstring, xml, json
- Disk space: Minimum 500MB, scales with traffic volume
- Memory: Minimum 128MB PHP memory limit (256MB recommended)
Installation Steps:
- Download latest Matomo from matomo.org/download
- Upload files to web server via FTP/SSH
- Create MySQL database and user
- Run web installer at
https://your-domain.com/matomo/ - Complete setup wizard (database, admin account, first site)
- Obtain tracking code from Administration panel
Technical Requirements
- Web: Modern browsers (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+)
- Mobile: iOS 10+ or Android 5.0+ (API level 21+)
- Server-side: PHP 7.2+, Python 3.6+, Node.js 12+, Java 8+, or any HTTP client
- Package managers: npm 6+, yarn 1.22+, CocoaPods 1.10+, Gradle 6+
Security Considerations
- Use HTTPS for all Matomo tracking endpoints
- Store site IDs and auth tokens in environment variables
- Enable two-factor authentication for Matomo admin accounts
- Configure IP anonymization for EU/EEA visitors
- Implement proper consent management for cookie-based tracking
- Regularly update Matomo to latest security patches
JavaScript Tracking Code
Standard Installation
The most common installation method is embedding the Matomo JavaScript tracking code in your site's <head> or before the closing </body> tag:
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//your-matomo-domain.com/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<!-- End Matomo Code -->
Obtain your tracking code:
- Matomo Dashboard → Administration → Websites → Tracking Code
- Copy the generated code specific to your site ID
Placement recommendations:
- Header placement: Place in
<head>for early initialization and complete session tracking - Footer placement: Place before
</body>for faster initial page render - Tag Manager: Load via GTM or Matomo Tag Manager for centralized management
Environment-Specific Configuration
Use server-side templating to inject correct site IDs per environment:
<!-- PHP example -->
<script>
var _paq = window._paq = window._paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//<?php echo $_ENV['MATOMO_URL']; ?>/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '<?php echo $_ENV['MATOMO_SITE_ID']; ?>']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
// Node.js/Express example
app.get('*', (req, res) => {
res.render('template', {
matomoUrl: process.env.MATOMO_URL,
matomoSiteId: process.env.MATOMO_SITE_ID
});
});
Configuration Options
Customize tracking behavior by adding commands before trackPageView:
var _paq = window._paq = window._paq || [];
// Disable cookies for cookieless tracking
_paq.push(['disableCookies']);
// Enable Do Not Track detection
_paq.push(['setDoNotTrack', true]);
// Set custom dimensions
_paq.push(['setCustomDimension', 1, 'Premium Member']);
_paq.push(['setCustomDimension', 2, 'US-East']);
// Track subdomains in same visit
_paq.push(['setCookieDomain', '*.example.com']);
_paq.push(['setDomains', ['*.example.com', '*.example.org']]);
// Anonymize visitor IP addresses
_paq.push(['setVisitorCookieTimeout', 13 * 30 * 24 * 60 * 60]); // 13 months
// Enable HeartBeat timer for accurate time on page
_paq.push(['enableHeartBeatTimer', 15]); // Send ping every 15 seconds
// Track page view
_paq.push(['trackPageView']);
// Enable automatic link tracking
_paq.push(['enableLinkTracking']);
Cookieless Tracking
For maximum privacy compliance, enable cookieless tracking:
var _paq = window._paq = window._paq || [];
// Disable all cookies
_paq.push(['disableCookies']);
// Alternative: Require consent before setting cookies
_paq.push(['requireConsent']);
// Track page view
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
When consent is given:
// Enable cookies after consent
_paq.push(['rememberConsentGiven']);
// Or for limited time consent (e.g., 1 year)
_paq.push(['rememberConsentGiven', 8760]); // hours
Consent Management Integration
Integrate with consent management platforms:
// Check consent before tracking
function initializeMatomoWithConsent() {
var _paq = window._paq = window._paq || [];
// Wait for consent
_paq.push(['requireConsent']);
_paq.push(['requireCookieConsent']);
// Basic configuration
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//your-matomo-domain.com/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
}
// Call on page load
initializeMatomoWithConsent();
// When user consents
function onUserConsent() {
_paq.push(['setConsentGiven']);
_paq.push(['setCookieConsentGiven']);
}
// When user revokes consent
function onConsentRevoked() {
_paq.push(['forgetConsentGiven']);
_paq.push(['forgetCookieConsentGiven']);
}
Matomo Tag Manager
Matomo's built-in Tag Manager provides an alternative to direct tracking code installation.
Installation Steps
Create Matomo Tag Manager container:
- Navigate to Tag Manager → Manage Containers → Create New Container
- Select container type: Web
- Name your container (e.g., "Production Web")
Get container embed code:
<!-- Matomo Tag Manager -->
<script>
var _mtm = window._mtm = window._mtm || [];
_mtm.push({'mtm.startTime': (new Date().getTime()), 'event': 'mtm.Start'});
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src='https://your-matomo-domain.com/js/container_CONTAINER_ID.js'; s.parentNode.insertBefore(g,s);
</script>
<!-- End Matomo Tag Manager -->
Configure Matomo Analytics tag:
- Tag Type: Matomo Analytics
- Matomo Configuration: Create new configuration
- Matomo URL:
https://your-matomo-domain.com - Site ID: Your site ID
- Trigger: Page View - All Pages
Create variables for dynamic configuration:
- Variable Name:
Matomo Site ID - Variable Type: Constant
- Value:
{{Environment Site ID}}
- Variable Name:
Set up triggers:
- Page View: All pages
- Click: All link clicks (for download/outlink tracking)
- Custom Events: Based on data layer events
Environment Management
Create separate containers or use variables:
// Environment-based configuration
var _mtm = window._mtm = window._mtm || [];
_mtm.push({
'mtm.startTime': (new Date().getTime()),
'event': 'mtm.Start',
'environment': '{{ ENVIRONMENT }}', // dev, staging, production
'siteId': '{{ SITE_ID }}'
});
Data Layer Integration
// Push data layer events
var _mtm = window._mtm = window._mtm || [];
_mtm.push({
'event': 'purchase',
'ecommerce': {
'purchase': {
'actionField': {
'id': 'T12345',
'revenue': '149.99',
'tax': '12.50',
'shipping': '5.00'
},
'products': [{
'name': 'Product Name',
'id': 'P12345',
'price': '49.99',
'category': 'Apparel',
'quantity': 3
}]
}
}
});
NPM Package Installation
For modern JavaScript applications, use the Matomo npm package:
npm install @jonkoops/matomo-tracker
# or
yarn add @jonkoops/matomo-tracker
Basic Implementation
import MatomoTracker from '@jonkoops/matomo-tracker';
// Initialize tracker
const tracker = new MatomoTracker({
urlBase: 'https://your-matomo-domain.com',
siteId: 1,
trackerUrl: 'https://your-matomo-domain.com/matomo.php',
srcUrl: 'https://your-matomo-domain.com/matomo.js',
disabled: false, // Set to true to disable tracking
heartBeat: {
active: true,
seconds: 15
},
linkTracking: true,
configurations: {
disableCookies: false,
setSecureCookie: true,
setRequestMethod: 'POST'
}
});
// Track page view
tracker.trackPageView();
// Track custom event
tracker.trackEvent({
category: 'Video',
action: 'Play',
name: 'Product Demo',
value: 60
});
// Track ecommerce order
tracker.trackEcommerceOrder({
orderId: 'order-123',
orderRevenue: 149.99,
orderSubTotal: 135.00,
orderTax: 9.99,
orderShipping: 5.00,
orderDiscount: 0
});
Environment-Specific Configuration
// config/matomo.js
const matomoConfig = {
development: {
urlBase: process.env.MATOMO_DEV_URL,
siteId: parseInt(process.env.MATOMO_DEV_SITE_ID),
disabled: false,
configurations: {
disableCookies: true
}
},
staging: {
urlBase: process.env.MATOMO_STAGING_URL,
siteId: parseInt(process.env.MATOMO_STAGING_SITE_ID),
disabled: false
},
production: {
urlBase: process.env.MATOMO_PROD_URL,
siteId: parseInt(process.env.MATOMO_PROD_SITE_ID),
configurations: {
setSecureCookie: true,
setRequestMethod: 'POST'
}
}
};
const env = process.env.NODE_ENV || 'development';
export const tracker = new MatomoTracker(matomoConfig[env]);
React Integration
Using Matomo Tracker React
Install the React-specific package:
npm install @datapunt/matomo-tracker-react
# or
yarn add @datapunt/matomo-tracker-react
Setup Provider
// src/index.js or src/App.js
import { MatomoProvider, createInstance } from '@datapunt/matomo-tracker-react';
const instance = createInstance({
urlBase: process.env.REACT_APP_MATOMO_URL,
siteId: parseInt(process.env.REACT_APP_MATOMO_SITE_ID),
disabled: false,
heartBeat: {
active: true,
seconds: 15
},
linkTracking: true,
configurations: {
disableCookies: process.env.REACT_APP_MATOMO_DISABLE_COOKIES === 'true',
setSecureCookie: true,
setRequestMethod: 'POST'
}
});
function App() {
return (
<MatomoProvider value={instance}>
{/* Your app */}
</MatomoProvider>
);
}
Using Hooks
// components/Checkout.js
import { useMatomo } from '@datapunt/matomo-tracker-react';
function Checkout() {
const { trackPageView, trackEvent } = useMatomo();
useEffect(() => {
trackPageView({
documentTitle: 'Checkout Page'
});
}, [trackPageView]);
const handlePurchase = (order) => {
trackEvent({
category: 'Ecommerce',
action: 'Purchase',
name: `Order ${order.id}`,
value: order.total
});
};
return (
<button => handlePurchase(orderData)}>
Complete Purchase
</button>
);
}
React Router Integration
// src/App.js
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { useMatomo } from '@datapunt/matomo-tracker-react';
function App() {
const location = useLocation();
const { trackPageView } = useMatomo();
useEffect(() => {
trackPageView({
documentTitle: document.title,
href: window.location.href
});
}, [location, trackPageView]);
return (
<Router>
{/* Your routes */}
</Router>
);
}
Vue.js Integration
Vue 3 Plugin
Create a Matomo plugin for Vue 3:
// plugins/matomo.js
import MatomoTracker from '@jonkoops/matomo-tracker';
export default {
install: (app, options) => {
const tracker = new MatomoTracker({
urlBase: options.urlBase,
siteId: options.siteId,
trackerUrl: `${options.urlBase}/matomo.php`,
srcUrl: `${options.urlBase}/matomo.js`,
linkTracking: true,
configurations: options.configurations || {}
});
// Add to global properties
app.config.globalProperties.$matomo = tracker;
// Add helper methods
app.config.globalProperties.$trackEvent = (category, action, name, value) => {
tracker.trackEvent({ category, action, name, value });
};
app.config.globalProperties.$trackPageView = (customTitle) => {
tracker.trackPageView({
documentTitle: customTitle || document.title
});
};
}
};
Register Plugin:
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import matomoPlugin from './plugins/matomo';
const app = createApp(App);
app.use(matomoPlugin, {
urlBase: import.meta.env.VITE_MATOMO_URL,
siteId: parseInt(import.meta.env.VITE_MATOMO_SITE_ID),
configurations: {
disableCookies: false,
setSecureCookie: true
}
});
app.mount('#app');
Usage:
<template>
<button @click="trackPurchase">Complete Purchase</button>
</template>
<script>
export default {
methods: {
trackPurchase() {
this.$trackEvent('Ecommerce', 'Purchase', `Order ${this.orderId}`, this.orderTotal);
}
},
mounted() {
this.$trackPageView('Checkout Page');
}
};
</script>
Vue Router Integration
// router/index.js
import { createRouter } from 'vue-router';
const router = createRouter({
// ... routes
});
router.afterEach((to, from) => {
if (window._paq) {
window._paq.push(['setCustomUrl', to.fullPath]);
window._paq.push(['setDocumentTitle', to.meta.title || document.title]);
window._paq.push(['trackPageView']);
}
});
export default router;
Angular Integration
Service-Based Approach
// services/matomo.service.ts
import { Injectable } from '@angular/core';
import { environment } from '../environments/environment';
declare global {
interface Window {
_paq: any[];
}
}
@Injectable({
providedIn: 'root'
})
export class MatomoService {
constructor() {
this.initialize();
}
private initialize(): void {
window._paq = window._paq || [];
window._paq.push(['trackPageView']);
window._paq.push(['enableLinkTracking']);
const script = document.createElement('script');
script.async = true;
script.src = `${environment.matomoUrl}/matomo.js`;
const firstScript = document.getElementsByTagName('script')[0];
firstScript.parentNode?.insertBefore(script, firstScript);
window._paq.push(['setTrackerUrl', `${environment.matomoUrl}/matomo.php`]);
window._paq.push(['setSiteId', environment.matomoSiteId]);
}
trackPageView(customTitle?: string): void {
if (customTitle) {
window._paq.push(['setDocumentTitle', customTitle]);
}
window._paq.push(['trackPageView']);
}
trackEvent(category: string, action: string, name?: string, value?: number): void {
window._paq.push(['trackEvent', category, action, name, value]);
}
trackGoal(goalId: number, customRevenue?: number): void {
window._paq.push(['trackGoal', goalId, customRevenue]);
}
setCustomDimension(dimensionId: number, value: string): void {
window._paq.push(['setCustomDimension', dimensionId, value]);
}
setUserId(userId: string): void {
window._paq.push(['setUserId', userId]);
}
resetUserId(): void {
window._paq.push(['resetUserId']);
}
}
Environment Configuration:
// environments/environment.prod.ts
export const environment = {
production: true,
matomoUrl: 'https://your-matomo-domain.com',
matomoSiteId: 1
};
// environments/environment.ts
export const environment = {
production: false,
matomoUrl: 'https://dev-matomo-domain.com',
matomoSiteId: 2
};
Router Integration:
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { MatomoService } from './services/matomo.service';
import { filter } from 'rxjs/operators';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
constructor(
private router: Router,
private matomo: MatomoService
) {}
ngOnInit(): void {
this.router.events
.pipe(filter(event => event instanceof NavigationEnd))
.subscribe((event: NavigationEnd) => {
this.matomo.trackPageView(document.title);
});
}
}
Next.js Integration
App Router (Next.js 13+)
// components/MatomoProvider.tsx
'use client';
import { useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';
declare global {
interface Window {
_paq: any[];
}
}
export default function MatomoProvider({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
// Initialize Matomo
window._paq = window._paq || [];
window._paq.push(['trackPageView']);
window._paq.push(['enableLinkTracking']);
const script = document.createElement('script');
script.async = true;
script.src = `${process.env.NEXT_PUBLIC_MATOMO_URL}/matomo.js`;
document.head.appendChild(script);
window._paq.push(['setTrackerUrl', `${process.env.NEXT_PUBLIC_MATOMO_URL}/matomo.php`]);
window._paq.push(['setSiteId', process.env.NEXT_PUBLIC_MATOMO_SITE_ID]);
}, []);
useEffect(() => {
// Track route changes
if (window._paq) {
window._paq.push(['setCustomUrl', pathname]);
window._paq.push(['trackPageView']);
}
}, [pathname, searchParams]);
return <>{children}</>;
}
Add to Root Layout:
// app/layout.tsx
import MatomoProvider from '@/components/MatomoProvider';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<MatomoProvider>
{children}
</MatomoProvider>
</body>
</html>
);
}
Mobile SDKs
iOS (Swift)
Installation via CocoaPods:
# Podfile
platform :ios, '11.0'
target 'YourApp' do
use_frameworks!
pod 'MatomoTracker', '~> 7.5'
end
pod install
Initialize:
// AppDelegate.swift
import MatomoTracker
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let matomoURL = URL(string: "https://your-matomo-domain.com/matomo.php")!
#if DEBUG
MatomoTracker.shared.siteId = "2" // Dev site ID
#else
MatomoTracker.shared.siteId = "1" // Production site ID
#endif
MatomoTracker.shared.tracker = URLSessionDispatcher(baseURL: matomoURL)
MatomoTracker.shared.logger = DefaultLogger(minLevel: .verbose)
return true
}
}
Track Events:
// Track screen view
MatomoTracker.shared.track(view: ["Home", "Dashboard"])
// Track event
MatomoTracker.shared.track(
eventWithCategory: "Ecommerce",
action: "Purchase",
name: "Product ABC",
value: 49.99
)
// Track goal
MatomoTracker.shared.track(goal: 1, revenue: 149.99)
// Set custom dimensions
MatomoTracker.shared.set(value: "Premium", forIndex: 1)
// Set user ID
MatomoTracker.shared.userId = "user_123"
Android (Kotlin)
Installation via Gradle:
// build.gradle (app level)
dependencies {
implementation 'org.matomo.sdk:tracker:4.1.4'
}
Initialize:
// MainApplication.kt
import android.app.Application
import org.matomo.sdk.Tracker
import org.matomo.sdk.TrackerBuilder
import org.matomo.sdk.extra.TrackHelper
class MainApplication : Application() {
companion object {
lateinit var tracker: Tracker
}
override fun onCreate() {
super.onCreate()
val siteId = if (BuildConfig.DEBUG) 2 else 1
tracker = TrackerBuilder.createDefault(
"https://your-matomo-domain.com/matomo.php",
siteId
).build(Matomo.getInstance(this))
}
}
Track Events:
// Track screen view
TrackHelper.track()
.screen("/home/dashboard")
.title("Dashboard")
.with(MainApplication.tracker)
// Track event
TrackHelper.track()
.event("Ecommerce", "Purchase")
.name("Product ABC")
.value(49.99f)
.with(MainApplication.tracker)
// Track goal
TrackHelper.track()
.goal(1)
.revenue(149.99f)
.with(MainApplication.tracker)
// Set custom dimension
tracker.setCustomDimension(1, "Premium")
// Set user ID
tracker.userId = "user_123"
React Native
npm install react-native-matomo-sdk
# iOS only:
cd ios && pod install && cd ..
Initialize:
// App.js
import Matomo from 'react-native-matomo-sdk';
const App = () => {
useEffect(() => {
Matomo.initialize({
baseUrl: 'https://your-matomo-domain.com',
siteId: __DEV__ ? 2 : 1,
userId: null,
disabled: false
});
}, []);
return <YourApp />;
};
Track Events:
// Track screen view
Matomo.trackScreen('/home/dashboard', 'Dashboard');
// Track event
Matomo.trackEvent('Ecommerce', 'Purchase', 'Product ABC', 49.99);
// Track goal
Matomo.trackGoal(1, 149.99);
// Set custom dimension
Matomo.setCustomDimension(1, 'Premium');
// Set user ID
Matomo.setUserId('user_123');
Server-Side Tracking
Tracking API (HTTP)
Send events directly to Matomo's tracking endpoint:
curl -X POST \
'https://your-matomo-domain.com/matomo.php' \
-d 'idsite=1' \
-d 'rec=1' \
-d 'action_name=Page%20Title' \
-d 'url=https://example.com/page' \
-d 'uid=user_123' \
-d '_id=visitor_id_hex' \
-d 'token_auth=YOUR_TOKEN_AUTH'
Required parameters:
idsite: Site IDrec: Must be set to 1action_name: Page title or action nameurl: Full URL being tracked
Optional parameters:
uid: User ID for identified visitors_id: Visitor ID (16-character hexadecimal)token_auth: Authentication token (for server-side)e_c: Event categorye_a: Event actione_n: Event namee_v: Event value
Node.js
npm install matomo-tracker
const MatomoTracker = require('matomo-tracker');
const matomo = new MatomoTracker(1, 'https://your-matomo-domain.com/matomo.php');
// Track page view
matomo.track({
url: 'https://example.com/page',
action_name: 'Page Title',
ua: 'User-Agent String',
uid: 'user_123',
cvar: JSON.stringify({
'1': ['Membership', 'Premium'],
'2': ['Region', 'US-East']
})
});
// Track event
matomo.track({
url: 'https://example.com/page',
action_name: 'Event Tracking',
e_c: 'Video',
e_a: 'Play',
e_n: 'Product Demo',
e_v: 60,
uid: 'user_123'
});
// Track ecommerce order
matomo.track({
idgoal: 0, // 0 = ecommerce order
ec_id: 'order_123',
revenue: 149.99,
ec_st: 135.00, // subtotal
ec_tx: 9.99, // tax
ec_sh: 5.00, // shipping
ec_items: JSON.stringify([[
'SKU123', // SKU
'Product Name', // Name
'Category', // Category
49.99, // Price
3 // Quantity
]])
});
Python
pip install matomo-python-tracker
from matomo_tracker import Tracker
tracker = Tracker('https://your-matomo-domain.com', 1)
# Track page view
tracker.track_page_view(
'https://example.com/page',
'Page Title',
uid='user_123',
custom_vars={
'1': ('Membership', 'Premium'),
'2': ('Region', 'US-East')
}
)
# Track event
tracker.track_event(
category='Video',
action='Play',
name='Product Demo',
value=60,
uid='user_123'
)
# Track ecommerce order
tracker.track_ecommerce_order(
order_id='order_123',
revenue=149.99,
subtotal=135.00,
tax=9.99,
shipping=5.00,
discount=0,
items=[[
'SKU123', # SKU
'Product Name', # Name
'Category', # Category
49.99, # Price
3 # Quantity
]]
)
PHP
<?php
// Use Matomo's PHP Tracking API
require_once 'path/to/MatomoTracker.php';
$tracker = new MatomoTracker(1, 'https://your-matomo-domain.com/matomo.php');
// Track page view
$tracker->setUrl('https://example.com/page');
$tracker->setPageTitle('Page Title');
$tracker->setUserId('user_123');
$tracker->setCustomVariable(1, 'Membership', 'Premium', 'visit');
$tracker->doTrackPageView('Page Title');
// Track event
$tracker->doTrackEvent('Video', 'Play', 'Product Demo', 60);
// Track ecommerce order
$tracker->addEcommerceItem(
'SKU123', // SKU
'Product Name', // Name
'Category', // Category
49.99, // Price
3 // Quantity
);
$tracker->doTrackEcommerceOrder(
'order_123', // Order ID
149.99, // Grand total
135.00, // Subtotal
9.99, // Tax
5.00, // Shipping
0 // Discount
);
?>
Google Tag Manager Deployment
Deploy Matomo through GTM for centralized tag management:
Setup Steps
Create Custom HTML Tag:
- Tag Type: Custom HTML
- Tag Name: "Matomo Analytics - Initialization"
Insert Matomo Code:
<script>
var _paq = window._paq = window._paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//{{Matomo URL}}/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '{{Matomo Site ID}}']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
Create GTM Variables:
Variable Name:
Matomo URLVariable Type: Constant or Lookup Table
Value:
https://your-matomo-domain.comVariable Name:
Matomo Site IDVariable Type: Lookup Table (for multi-environment)
Input:
{{Environment}}Output: dev → 2, staging → 3, production → 1
Configure Trigger:
- Trigger Type: Page View - DOM Ready
- Fire On: All Pages
Create Event Tracking Tags:
<!-- Custom Event Tag -->
<script>
if (window._paq) {
_paq.push(['trackEvent',
'{{Event Category}}',
'{{Event Action}}',
'{{Event Name}}',
{{Event Value}}
]);
}
</script>
Verification Steps
1. Real-Time Visitor Log
- Navigate to Visitors → Real-time in Matomo dashboard
- Perform test actions on your site
- Verify visits appear within 5 seconds
- Check page titles, URLs, and custom dimensions
2. Tracking Debug Console
Enable tracking debug mode:
_paq.push(['enableDebugMode']);
Or append ?pk_debug=1 to your URL and check browser console for Matomo debug output.
3. Visitor Profile Validation
- Navigate to Visitors → Visitor Log
- Find your test session
- Verify:
- User ID is set correctly
- Custom dimensions appear
- Events are tracked
- Ecommerce data is recorded
4. Tag Assistant / Browser DevTools
Check network requests:
- Look for requests to
matomo.php - Verify correct
idsiteparameter - Confirm tracking parameters are present
Troubleshooting
Events Not Tracking
Check Matomo status:
console.log(window._paq); // Should be array of tracking calls
Common issues:
- Incorrect site ID: Verify
idsitematches dashboard - CORS errors: Ensure tracking domain allows cross-origin requests
- Ad blockers: Test in incognito or whitelist Matomo domain
- JavaScript errors: Check browser console for errors before Matomo initialization
- Cookie restrictions: Verify cookies are allowed or use cookieless tracking
Visits Not Appearing
Possible causes:
- Do Not Track: Matomo respects DNT by default (disable with
setDoNotTrack(false)) - IP exclusion: Your IP may be in excluded IP list (Administration → Websites → Edit → Settings)
- Bot detection: Matomo filters known bots (Administration → System → General Settings)
- Archiving delay: Reports update every hour by default (check Real-time for immediate data)
Cross-Domain Tracking Issues
Configure cookie domain:
_paq.push(['setCookieDomain', '*.example.com']);
_paq.push(['setDomains', ['*.example.com', '*.shop.example.com']]);
Verify cookies:
- Check
_pk_idcookie is set with correct domain scope - Ensure visitor ID persists across domains
Performance Impact
Optimize tracking:
// Defer Matomo initialization
setTimeout(function() {
// Matomo tracking code here
}, 1000);
// Reduce HeartBeat frequency
_paq.push(['enableHeartBeatTimer', 30]); // 30 seconds instead of 15
// Disable link tracking if not needed
_paq.push(['disablePerformanceTracking']);
Security Best Practices
1. Use HTTPS
Always serve Matomo over HTTPS:
var u="https://your-matomo-domain.com/"; // Always use https://
2. Secure Authentication Tokens
For server-side tracking, protect auth tokens:
// Never expose in client-side code
_paq.push(['setAuthToken', 'secret_token']);
// Only use in server-side tracking
// Keep token in environment variables
const token = process.env.MATOMO_AUTH_TOKEN;
3. IP Anonymization
Enable IP masking for privacy:
// Anonymize last 2 bytes of IPv4 (e.g., 192.168.1.x)
_paq.push(['setIpAnonymization', 2]);
Or configure in Matomo admin: Administration → Privacy → Anonymize Visitors' IP addresses
4. Respect Do Not Track
_paq.push(['setDoNotTrack', true]); // Respect browser DNT header
5. Data Retention
Configure retention policies: Administration → Privacy → Anonymize Old Visits
- Delete visitor logs after X days
- Delete reports after X months
Performance Optimization
1. Async Loading
Matomo loads asynchronously by default. Verify:
g.async=true; // Should always be true
2. CDN or Caching
For self-hosted Matomo:
- Enable CDN for
matomo.js - Set long cache headers for static assets
- Use HTTP/2 for faster resource loading
3. Batch Tracking Requests
For high-traffic sites:
_paq.push(['setRequestMethod', 'POST']);
_paq.push(['enableBulkTracking']);
4. Disable Features Not Used
// Disable performance tracking
_paq.push(['disablePerformanceTracking']);
// Disable link tracking if not needed
// (Remove enableLinkTracking call)
Advanced Configuration
Custom Dimensions
Define and track custom data:
// Configure in Matomo: Administration → Websites → Custom Dimensions
_paq.push(['setCustomDimension', 1, 'Premium Member']);
_paq.push(['setCustomDimension', 2, 'US-East']);
_paq.push(['trackPageView']);
Content Tracking
Track specific content blocks:
<div data-track-content data-content-name="Banner Ad" data-content-piece="Holiday Sale">
<a href="/sale" data-content-target="/sale">
<img src="banner.jpg" alt="Sale" />
</a>
</div>
_paq.push(['trackAllContentImpressions']);
_paq.push(['trackContentImpressionsWithinNode', document.getElementById('content')]);
Form Analytics
Track form interactions (requires Form Analytics plugin):
_paq.push(['FormAnalytics::scanForForms']);
_paq.push(['FormAnalytics::trackForm', 'signup-form']);
Heatmaps & Session Recording
Enable visual analytics (requires Heatmap & Session Recording plugin):
// Automatically tracked when plugin is installed
// Configure sample rate in plugin settings
Configuration Recommendations
Self-Hosted vs. Cloud: Self-host if you need full data ownership, unlimited data retention, or must comply with regulations requiring data to stay on your infrastructure. Use Matomo Cloud if you want managed hosting with automatic updates and backups. Self-hosting requires a LAMP/LEMP stack with at least 2GB RAM for sites under 100K monthly pageviews; 8GB+ for high-traffic sites.
Cookie Strategy: Use cookieless tracking (_paq.push(['disableCookies'])) if you want to avoid consent requirements entirely — Matomo still tracks visits using a fingerprint hash that expires each session. If you need returning visitor identification, use first-party cookies with your CMP. Cookie consent is required under GDPR; cookieless mode is not.
IP Anonymization: Mask at least 2 bytes (Privacy → Anonymize Data → Mask 2 bytes) to comply with GDPR. Full 4-byte masking (completely anonymized) eliminates geographic reporting below country level. The default is 1 byte, which is insufficient for most EU privacy authorities.
Cross-Domain Tracking: Enable in Administration → Websites → Settings. Add all domains and subdomains that should share a visitor ID. Matomo uses first-party cookies and link decoration (pk_ parameters) for cross-domain tracking. Ensure your CMP covers all tracked domains.
Tag Manager vs. Direct: Use Matomo Tag Manager if you manage multiple tracking tools — it eliminates the need for GTM and keeps all data within your Matomo instance. Use direct tracking code if Matomo is your only analytics tool and you want the simplest setup.
Auto-Archiving: For sites with more than 50K monthly pageviews, disable browser-triggered archiving and set up a cron job: */5 * * * * php /path/to/matomo/console core:archive. This prevents slow report loading for dashboard users.