Overview
Plausible Analytics is a lightweight, privacy-first analytics platform that doesn't use cookies and respects user privacy by default. The Plausible script weighs less than 1KB (compared to Google Analytics at 45KB+), making it one of the fastest analytics solutions available. This guide covers all deployment methods across web, mobile, and server environments.
Key Benefits:
- No cookies or persistent identifiers
- GDPR, CCPA, and PECR compliant by default
- Script size < 1KB for optimal performance
- No impact on Core Web Vitals
- Open source and self-hostable
Prerequisites
Before installing Plausible, ensure you have:
Active Plausible Account
- Sign up at plausible.io or deploy self-hosted instance
- Add your domain to your Plausible account under Site Settings
- Note your exact domain as configured (e.g.,
yourdomain.comvswww.yourdomain.com)
Domain Configuration
Environment Planning
- Separate Plausible sites for staging, development, and production
- Document which domains map to which Plausible sites
- Plan first-party proxy strategy if avoiding ad blockers is critical
Access Requirements
- Ability to modify HTML
<head>section or deploy via tag manager - For npm installations: Node.js 14+ and package manager access
- For server-side: API access and authentication credentials
- Ability to modify HTML
Privacy & Compliance
- Review whether consent management is required for your jurisdiction
- Determine if IP anonymization or proxy requirements apply
- Document data retention policies
Installation Methods
Method 1: Direct JavaScript Snippet (Recommended)
The simplest and most common installation method. Add the script to your site's <head> section.
Basic Installation
<!DOCTYPE html>
<html>
<head>
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.js"></script>
<!-- Rest of your head content -->
</head>
<body>
<!-- Your content -->
</body>
</html>
Important Attributes:
defer: Loads script asynchronously without blocking page renderdata-domain: Must match your domain exactly as configured in Plausible- Script automatically tracks pageviews on load and history changes
Self-Hosted Installation
If you're running Plausible on your own infrastructure:
<script defer data-domain="yourdomain.com" src="https://analytics.yourdomain.com/js/script.js"></script>
Replace analytics.yourdomain.com with your self-hosted Plausible instance URL.
Extended Script Variants
Plausible offers specialized script variants for different use cases:
Hash-based routing (for SPAs):
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.hash.js"></script>
Outbound link tracking:
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.outbound-links.js"></script>
File downloads tracking:
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.file-downloads.js"></script>
404 error tracking:
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.404.js"></script>
Multiple extensions (combine features):
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.hash.outbound-links.file-downloads.js"></script>
Method 2: First-Party Proxy (Bypass Ad Blockers)
Ad blockers can prevent the Plausible script from loading. A first-party proxy routes requests through your domain.
Why Use a Proxy?
- Bypasses ad blockers that block third-party analytics
- Keeps all analytics traffic on your domain
- Improves data accuracy by 10-30% in some cases
- Maintains privacy-first approach
Proxy Setup Options
Option A: Using Cloudflare Workers
- Create a new Cloudflare Worker:
// cloudflare-worker.js
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url)
// Proxy script requests
if (url.pathname === '/js/script.js') {
return fetch('https://plausible.io/js/script.js')
}
// Proxy API events
if (url.pathname === '/api/event') {
const newRequest = new Request('https://plausible.io/api/event', request)
return fetch(newRequest)
}
return fetch(request)
}
- Update your HTML to use the proxy:
<script defer data-domain="yourdomain.com" data-api="/api/event" src="/js/script.js"></script>
Option B: Using Nginx Reverse Proxy
# nginx.conf
location = /js/script.js {
proxy_pass https://plausible.io/js/script.js;
proxy_set_header Host plausible.io;
proxy_ssl_server_name on;
proxy_ssl_name plausible.io;
}
location = /api/event {
proxy_pass https://plausible.io/api/event;
proxy_set_header Host plausible.io;
proxy_ssl_server_name on;
proxy_ssl_name plausible.io;
proxy_buffering on;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Option C: Using Apache Reverse Proxy
# .htaccess or apache config
ProxyPass /js/script.js https://plausible.io/js/script.js
ProxyPassReverse /js/script.js https://plausible.io/js/script.js
ProxyPass /api/event https://plausible.io/api/event
ProxyPassReverse /api/event https://plausible.io/api/event
Method 3: NPM Package Integration
For modern JavaScript applications with build processes.
Installation
npm install plausible-tracker
# or
yarn add plausible-tracker
# or
pnpm add plausible-tracker
Basic Usage
import Plausible from 'plausible-tracker'
const plausible = Plausible({
domain: 'yourdomain.com',
apiHost: 'https://plausible.io', // Optional: use your self-hosted instance
})
// Enable automatic pageview tracking
plausible.enableAutoPageviews()
// Track custom events
plausible.trackEvent('signup', {
props: {
plan: 'premium',
method: 'google'
}
})
Advanced Configuration
const plausible = Plausible({
domain: 'yourdomain.com',
apiHost: 'https://plausible.io',
trackLocalhost: false, // Don't track localhost
hashMode: true, // Enable hash-based routing
})
// Manual pageview tracking
plausible.trackPageview({
url: 'https://yourdomain.com/custom-page',
referrer: document.referrer,
deviceWidth: window.innerWidth,
})
// Track with custom properties
plausible.trackEvent('purchase', {
props: {
amount: 99.99,
currency: 'USD',
product: 'subscription'
},
revenue: {
currency: 'USD',
amount: 99.99
}
})
Method 4: Google Tag Manager Deployment
Deploy Plausible through GTM for centralized tag management.
Step 1: Create Custom HTML Tag
- In GTM, navigate to Tags → New → Tag Configuration
- Select Custom HTML tag type
- Add the Plausible script:
<script>
(function() {
var script = document.createElement('script');
script.defer = true;
script.setAttribute('data-domain', '{{Page Hostname}}');
script.src = 'https://plausible.io/js/script.js';
document.head.appendChild(script);
})();
</script>
Step 2: Configure Trigger
- Trigger Type: Page View - DOM Ready
- This trigger fires on: All Pages
- Priority: Set high priority (e.g., 100) to load early
Step 3: Environment-Specific Configuration
Create a GTM Variable for the domain:
- Variables → New → Data Layer Variable
- Name:
Plausible Domain - Logic:
function() {
var hostname = {{Page Hostname}};
// Map staging/dev domains to separate Plausible sites
if (hostname.includes('staging')) {
return 'staging.yourdomain.com';
} else if (hostname.includes('localhost')) {
return 'dev.yourdomain.com';
}
return 'yourdomain.com';
}
Step 4: Custom Event Tracking via GTM
Create additional tags for custom events:
<script>
window.plausible = window.plausible || function() {
(window.plausible.q = window.plausible.q || []).push(arguments)
};
plausible('{{Event Name}}', {
props: {
category: '{{Event Category}}',
label: '{{Event Label}}',
value: '{{Event Value}}'
}
});
</script>
Framework Integrations
React Integration
Using Script Tag in index.html
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.js"></script>
<!-- Other head content -->
</head>
<body>
<div id="root"></div>
</body>
</html>
Using React Helmet
import { Helmet } from 'react-helmet';
function App() {
return (
<>
<Helmet>
<script
defer
data-domain="yourdomain.com"
src="https://plausible.io/js/script.js"
/>
</Helmet>
{/* Your app components */}
</>
);
}
Using NPM Package with React Hooks
import { useEffect } from 'react';
import Plausible from 'plausible-tracker';
const plausible = Plausible({
domain: 'yourdomain.com'
});
function App() {
useEffect(() => {
// Track pageviews on route changes
plausible.trackPageview();
}, []);
return <YourAppComponents />;
}
// Custom hook for event tracking
function usePlausible() {
const trackEvent = (eventName, props) => {
plausible.trackEvent(eventName, { props });
};
return { trackEvent };
}
// Usage in components
function SignupButton() {
const { trackEvent } = usePlausible();
const handleClick = () => {
trackEvent('signup-click', {
location: 'header',
plan: 'free'
});
};
return <button Up</button>;
}
Next.js Integration
App Router (Next.js 13+)
// app/layout.js
import Script from 'next/script'
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<Script
defer
data-domain="yourdomain.com"
src="https://plausible.io/js/script.js"
strategy="afterInteractive"
/>
</head>
<body>{children}</body>
</html>
)
}
Pages Router (Next.js 12 and earlier)
// pages/_app.js
import Script from 'next/script'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
// Track pageviews on route changes
const handleRouteChange = () => {
window.plausible?.('pageview')
}
router.events.on('routeChangeComplete', handleRouteChange)
return () => router.events.off('routeChangeComplete', handleRouteChange)
}, [router.events])
return (
<>
<Script
defer
data-domain="yourdomain.com"
src="https://plausible.io/js/script.js"
strategy="afterInteractive"
/>
<Component {...pageProps} />
</>
)
}
export default MyApp
Using next-plausible Package
npm install next-plausible
// pages/_app.js
import PlausibleProvider from 'next-plausible'
export default function MyApp({ Component, pageProps }) {
return (
<PlausibleProvider domain="yourdomain.com">
<Component {...pageProps} />
</PlausibleProvider>
)
}
// Track events in components
import { usePlausible } from 'next-plausible'
function MyComponent() {
const plausible = usePlausible()
const handleClick = () => {
plausible('custom-event', {
props: { buttonId: 'cta-main' }
})
}
return <button Me</button>
}
Vue.js Integration
Vue 3 Composition API
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import Plausible from 'plausible-tracker'
const plausible = Plausible({
domain: 'yourdomain.com'
})
const app = createApp(App)
// Make plausible available globally
app.config.globalProperties.$plausible = plausible
// Enable auto pageviews
plausible.enableAutoPageviews()
app.mount('#app')
<!-- Component.vue -->
<template>
<button @click="trackClick">Track Event</button>
</template>
<script setup>
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance()
const plausible = instance.appContext.config.globalProperties.$plausible
const trackClick = () => {
plausible.trackEvent('button-click', {
props: { component: 'hero' }
})
}
</script>
Vue 2 Options API
// main.js
import Vue from 'vue'
import App from './App.vue'
import Plausible from 'plausible-tracker'
const plausible = Plausible({
domain: 'yourdomain.com'
})
Vue.prototype.$plausible = plausible
plausible.enableAutoPageviews()
new Vue({
render: h => h(App),
}).$mount('#app')
Angular Integration
Add Script to index.html
<!-- src/index.html -->
<!doctype html>
<html lang="en">
<head>
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.js"></script>
</head>
<body>
<app-root></app-root>
</body>
</html>
Create Plausible Service
// services/plausible.service.ts
import { Injectable } from '@angular/core';
declare global {
interface Window {
plausible?: (event: string, options?: any) => void;
}
}
@Injectable({
providedIn: 'root'
})
export class PlausibleService {
trackEvent(eventName: string, props?: Record<string, any>) {
if (typeof window !== 'undefined' && window.plausible) {
window.plausible(eventName, { props });
}
}
trackPageview(url?: string) {
if (typeof window !== 'undefined' && window.plausible) {
window.plausible('pageview', url ? { u: url } : undefined);
}
}
}
// app.component.ts
import { Component } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';
import { PlausibleService } from './services/plausible.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
constructor(
private router: Router,
private plausible: PlausibleService
) {
// Track pageviews on route changes
this.router.events
.pipe(filter(event => event instanceof NavigationEnd))
.subscribe(() => {
this.plausible.trackPageview();
});
}
}
Svelte Integration
<!-- App.svelte -->
<svelte:head>
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.js"></script>
</svelte:head>
<script>
import { onMount } from 'svelte';
function trackEvent(eventName, props) {
if (typeof window !== 'undefined' && window.plausible) {
window.plausible(eventName, { props });
}
}
function handleClick() {
trackEvent('button-click', {
component: 'hero',
action: 'signup'
});
}
</script>
<button on:click={handleClick}>
Sign Up
</button>
Configuration Options
Script Attributes
Configure Plausible behavior using data attributes:
<script
defer
data-domain="yourdomain.com"
data-api="/api/event"
src="https://plausible.io/js/script.js"
></script>
Available Attributes:
| Attribute | Purpose | Example |
|---|---|---|
data-domain |
Your site domain (required) | yourdomain.com |
data-api |
Custom API endpoint for proxying | /api/event |
data-exclude |
Comma-separated pages to exclude | /admin,/dashboard |
data-include |
Only track specified pages | /blog,/docs |
Excluding Pages from Tracking
<script
defer
data-domain="yourdomain.com"
data-exclude="/admin/*,/dashboard/*,/internal/*"
src="https://plausible.io/js/script.exclusions.js"
></script>
Manual Event Tracking
// Basic custom event
plausible('signup');
// Event with properties
plausible('purchase', {
props: {
product: 'subscription',
plan: 'premium',
amount: 99
}
});
// Revenue tracking
plausible('purchase', {
revenue: {
currency: 'USD',
amount: 99.00
}
});
Mobile & Server-Side Implementation
Mobile Web
Mobile web uses the same JavaScript snippet as desktop. No special configuration needed.
Native Mobile Apps (iOS/Android)
Plausible doesn't offer native SDKs, but you can use the Events API:
// iOS - Swift
import Foundation
func trackPlausibleEvent(domain: String, name: String, url: String, props: [String: Any]? = nil) {
let endpoint = "https://plausible.io/api/event"
var body: [String: Any] = [
"domain": domain,
"name": name,
"url": url
]
if let props = props {
body["props"] = props
}
guard let jsonData = try? JSONSerialization.data(withJSONObject: body) else { return }
var request = URLRequest(url: URL(string: endpoint)!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData
URLSession.shared.dataTask(with: request).resume()
}
// Usage
trackPlausibleEvent(
domain: "app.yourdomain.com",
name: "screen_view",
url: "app://home",
props: ["platform": "ios", "version": "2.1.0"]
)
// Android - Kotlin
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
suspend fun trackPlausibleEvent(
domain: String,
name: String,
url: String,
props: Map<String, Any>? = null
) = withContext(Dispatchers.IO) {
val client = OkHttpClient()
val endpoint = "https://plausible.io/api/event"
val json = JSONObject().apply {
put("domain", domain)
put("name", name)
put("url", url)
props?.let { put("props", JSONObject(it)) }
}
val body = json.toString().toRequestBody("application/json".toMediaType())
val request = Request.Builder()
.url(endpoint)
.post(body)
.build()
client.newCall(request).execute()
}
// Usage
trackPlausibleEvent(
domain = "app.yourdomain.com",
name = "screen_view",
url = "app://home",
props = mapOf("platform" to "android", "version" to "2.1.0")
)
Server-Side Tracking
Node.js
const fetch = require('node-fetch');
async function trackServerEvent(domain, eventName, url, props = {}) {
const response = await fetch('https://plausible.io/api/event', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': 'YourApp/1.0'
},
body: JSON.stringify({
domain: domain,
name: eventName,
url: url,
props: props
})
});
return response.ok;
}
// Track backend events
await trackServerEvent(
'yourdomain.com',
'api_purchase',
'https://yourdomain.com/api/checkout',
{
product: 'premium',
amount: 99,
userId: 'user-123'
}
);
Python
import requests
def track_plausible_event(domain, event_name, url, props=None):
endpoint = 'https://plausible.io/api/event'
payload = {
'domain': domain,
'name': event_name,
'url': url
}
if props:
payload['props'] = props
response = requests.post(
endpoint,
json=payload,
headers={'User-Agent': 'YourApp/1.0'}
)
return response.ok
# Usage
track_plausible_event(
domain='yourdomain.com',
event_name='server_purchase',
url='https://yourdomain.com/checkout',
props={'amount': 99, 'plan': 'premium'}
)
Verification & Testing
1. Visual Verification
After installing, verify tracking in real-time:
- Visit your website
- Log in to Plausible dashboard
- Navigate to your site's dashboard
- Check the "Current Visitors" counter increments
- Verify your page appears in the realtime view
2. Network Inspection
Use browser DevTools to verify requests:
- Open DevTools (F12)
- Navigate to Network tab
- Filter by "event" or "script"
- Look for requests to:
- Script:
https://plausible.io/js/script.js(or your proxy path) - Events:
https://plausible.io/api/event(or your proxy path)
- Script:
Successful Event Request:
{
"domain": "yourdomain.com",
"name": "pageview",
"url": "https://yourdomain.com/page",
"referrer": "https://google.com",
"screen_width": 1920
}
Response (Success):
Status: 202 Accepted
3. Console Testing
Test custom events via browser console:
// Check if Plausible is loaded
typeof plausible !== 'undefined'
// Fire test event
plausible('test-event', {
props: {
test: true,
timestamp: Date.now()
}
})
// Check for errors
// Should see no console errors if working correctly
4. Goal Verification
If tracking custom events:
- Go to Settings → Goals in Plausible
- Add a custom event goal matching your event name
- Trigger the event on your site
- Check Goal Conversions in dashboard
- Verify event appears with correct properties
Troubleshooting
Script Not Loading
Problem: Plausible script fails to load
Solutions:
Check Content Security Policy (CSP) headers allow Plausible:
Content-Security-Policy: script-src 'self' https://plausible.ioVerify ad blocker isn't blocking the script (implement proxy if needed)
Check for CORS issues if self-hosting:
add_header Access-Control-Allow-Origin *;Ensure script URL is correct and accessible
Events Not Appearing
Problem: Pageviews or events don't show in dashboard
Solutions:
- Verify
data-domainmatches exactly what's configured in Plausible - Check network requests show 202 Accepted response
- Disable ad blockers during testing
- Ensure you're not on an excluded page
- Check localStorage isn't blocking (Plausible uses it for deduplication)
- Verify you've created the goal in Plausible dashboard for custom events
Duplicate Pageviews
Problem: Same pageview tracked multiple times
Solutions:
- Ensure script only loaded once (check for duplicate tags)
- For SPAs, don't use both auto-pageview AND manual tracking
- Check GTM isn't firing multiple times
- Verify router integration isn't double-tracking
Cross-Domain Tracking Issues
Problem: Sessions break across subdomains
Solution: Use consistent domain configuration:
<!-- Use root domain for all subdomains -->
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.js"></script>
All subdomains (app.yourdomain.com, www.yourdomain.com) will track to the same site.
Self-Hosted Connection Issues
Problem: Can't connect to self-hosted instance
Solutions:
- Verify instance is accessible:
curl https://your-plausible-domain.com/api/health - Check SSL certificates are valid
- Verify firewall rules allow traffic
- Check database connection in Plausible logs
- Ensure ClickHouse database is running
Security Best Practices
1. Use HTTPS Only
Always serve Plausible over HTTPS:
<!-- Good -->
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.js"></script>
<!-- Bad - Never use HTTP -->
<script defer data-domain="yourdomain.com" src="http://plausible.io/js/script.js"></script>
2. Content Security Policy
Add Plausible to your CSP headers:
Content-Security-Policy:
script-src 'self' https://plausible.io;
connect-src 'self' https://plausible.io;
For proxied installations:
Content-Security-Policy:
script-src 'self';
connect-src 'self';
3. Subresource Integrity (SRI)
While Plausible's script changes occasionally, you can pin specific versions with SRI:
<!-- Note: This may break when Plausible updates -->
<script
defer
data-domain="yourdomain.com"
src="https://plausible.io/js/script.js"
integrity="sha384-[hash]"
crossorigin="anonymous"
></script>
Recommendation: Use proxy method instead for better control.
4. Environment Separation
Never mix environments:
// Good - Environment-specific configuration
const plausibleDomain = process.env.NODE_ENV === 'production'
? 'yourdomain.com'
: 'staging.yourdomain.com';
5. API Security for Server-Side
When using the Events API from servers:
// Use environment variables
const PLAUSIBLE_DOMAIN = process.env.PLAUSIBLE_DOMAIN;
// Validate inputs
function trackEvent(name, url, props) {
if (!name || typeof name !== 'string') {
throw new Error('Invalid event name');
}
// Sanitize URL
const sanitizedUrl = new URL(url, PLAUSIBLE_DOMAIN).toString();
// Continue with tracking...
}
6. Rate Limiting
Implement client-side rate limiting to prevent abuse:
const eventQueue = [];
const RATE_LIMIT = 10; // events per second
const WINDOW = 1000; // 1 second
function rateLimitedTrack(eventName, props) {
const now = Date.now();
// Remove old events outside window
while (eventQueue.length && eventQueue[0] < now - WINDOW) {
eventQueue.shift();
}
// Check limit
if (eventQueue.length >= RATE_LIMIT) {
console.warn('Rate limit exceeded');
return;
}
eventQueue.push(now);
plausible(eventName, { props });
}
7. Privacy Considerations
Even though Plausible is privacy-first:
- Don't track PII: Never send email addresses, names, or sensitive data in event properties
- Document data flows: Maintain inventory of what data is collected
- Respect Do Not Track: Optionally honor DNT headers:
if (navigator.doNotTrack === '1') {
// Don't initialize Plausible
} else {
// Load Plausible normally
}
Advanced Topics
Custom Domains for Self-Hosted
When self-hosting, use a custom subdomain:
- Create DNS A record:
analytics.yourdomain.com → YOUR_SERVER_IP - Configure SSL certificate
- Update Plausible configuration
- Use in script:
src="https://analytics.yourdomain.com/js/script.js"
Revenue Tracking
Track monetary values with events:
plausible('purchase', {
revenue: {
currency: 'USD',
amount: 99.99
},
props: {
product: 'subscription',
plan: 'annual'
}
});
Hash-Based Routing for SPAs
For apps using hash-based routing (/#/page):
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.hash.js"></script>
This treats /#/page1 and /#/page2 as different pages.
Bot Filtering
Plausible automatically filters known bots, but you can add additional checks:
// Block tracking from common automation tools
if (navigator.webdriver || window.callPhantom || window._phantom) {
console.log('Bot detected, skipping analytics');
} else {
// Load Plausible normally
}
Validation Checklist
Before going live, verify:
- Script loads successfully in production environment
-
data-domainmatches Plausible site configuration exactly - Pageviews appear in realtime dashboard within 30 seconds
- Custom events appear under configured goals
- Environment separation working (staging vs production)
- Ad blocker bypass strategy tested if implemented
- CSP headers allow Plausible connections
- No console errors related to Plausible
- Mobile and desktop tracking both functional
- Cross-domain tracking works if needed
- Server-side events appearing if implemented
- Event properties showing correct values
- Documentation updated with implementation details
- Team trained on accessing dashboards
Configuration Recommendations
Proxy Setup: Implement a first-party proxy if your audience uses ad blockers heavily (tech, developer, or European audiences). Use Cloudflare Workers for the simplest setup — Plausible provides a ready-made worker script. For Nginx or Apache, proxy /js/script.js and /api/event to plausible.io. This typically recovers 15-25% of blocked pageviews.
Script Extensions: Combine extensions in a single script URL to minimize requests. Common combination: script.tagged-events.outbound-links.file-downloads.js. Add hash if you use hash-based routing (e.g., example.com/#/page). Add compat only if you need IE11 support. Full list at plausible.io/docs/script-extensions.
Privacy: Plausible is cookieless by default and does not require a consent banner in most jurisdictions. If your legal team requires explicit consent anyway, wrap the script load in your consent check. IP addresses are never stored — they're used only for unique visitor counting within a session and then discarded.
Goals: Create goals in Plausible dashboard (Site Settings → Goals) before deploying plausible('event_name') calls. Goals must exist in the dashboard to appear in reports. Revenue tracking requires the revenue property: plausible('Purchase', {revenue: {currency: 'USD', amount: 29.99}}).
Environment Separation: Create separate Plausible sites for production and staging (e.g., example.com and staging.example.com). Use data-domain attribute to target the correct site. Exclude localhost and dev environments by checking window.location.hostname before loading the script.