Website Analytics
Tydora's documentation site and landing page are static sites hosted on GitHub Pages (zuorn.github.io/Tydora) — there is no backend or database, so server logs cannot be used for traffic statistics. This article explains how to add visitor tracking to the site using client-side analytics, measuring PV (page views) and UV (unique visitors).
Why Track Visitors
- Understand which pages are most popular and identify trending documentation
- See where visitors come from and how they navigate, guiding content improvements
- Measure the impact of releases and announcements on traffic
Choosing an Analytics Service
Client-side analytics loads a small tracking script on every page, which reports visit data to an analytics service. Common free options:
| Service | Cost | PV/UV | Privacy | Access in China | Maintenance |
|---|---|---|---|---|---|
| Umami (Cloud free tier / self-hosted) | Free tier | ✅ | No cookies, privacy-friendly | Moderate | Medium |
| Baidu Analytics | Free | ✅ | China-compliant | Good | Low |
| Cloudflare Web Analytics | Free | ✅ | No cookies | Moderate | None |
| Plausible | From $9/mo | ✅ | No cookies | Poor | Low |
| Google Analytics 4 | Free | ✅ | Requires cookie banner | Poor | Low |
This project uses Umami (Umami Cloud free tier): open source, no lock-in, cookie-free and privacy-friendly, with a tiny script that does not affect loading performance, and full ownership of the data.
Setup Steps
1. Create a Website on Umami
- Visit umami.is and sign in with your GitHub account (the Umami Cloud free tier includes 10k events/month, enough for a personal documentation site)
- Click Add website:
- Name: any name, e.g.
Tydora - Domain: enter
zuorn.github.io— do not include the/Tydorapath (see FAQ below)
- Name: any name, e.g.
- After saving, open the website details and copy the Website ID (a UUID like
56c781b4-...)
2. Save the Tracking Script
Create website/analytics/snippet.html and paste the script provided by Umami, adding an id="t-analytics" attribute (used by the injector for idempotent de-duplication):
<!-- Tydora Analytics · Umami Cloud(https://umami.is) -->
<script id="t-analytics" defer src="https://cloud.umami.is/script.js" data-website-id="YOUR_WEBSITE_ID"></script>
This file is the single point of maintenance for tracking: to switch services later (e.g. Baidu Analytics), just replace the contents of this one file — no other code changes are needed.
3. Write the Injection Script
The documentation site is generated by markdown-publish (a static site generator) and does not support built-in analytics injection, so we walk through all HTML files after the build and insert the snippet before </head>.
Create scripts/inject-analytics.mjs:
/**
* Injects the tracking snippet before </head> in every HTML file under website/site/**
* Runs after copy-landing, so both landing pages and docs pages are covered
* The snippet is maintained in website/analytics/snippet.html (switch services by editing this one file)
* Idempotent: skips pages that already contain id="t-analytics", so repeated builds never double-inject
*/
import { readFileSync, writeFileSync, readdirSync, statSync } from "node:fs";
import { join, resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const siteDir = resolve(__dirname, "../website/site");
const SNIPPET_FILE = resolve(__dirname, "../website/analytics/snippet.html");
let snippet = "";
try {
snippet = readFileSync(SNIPPET_FILE, "utf-8").trim();
} catch {
console.log("⚠️ website/analytics/snippet.html not found, skipping injection");
process.exit(0);
}
function walk(dir) {
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
if (statSync(full).isDirectory()) walk(full);
else if (full.endsWith(".html")) inject(full);
}
}
function inject(file) {
const html = readFileSync(file, "utf-8");
if (!html.includes("</head>") || html.includes('id="t-analytics"')) return;
const out = html.replace("</head>", ` ${snippet}\n</head>`);
writeFileSync(file, out, "utf-8");
console.log(`✅ analytics injected → ${file.replace(siteDir, "site")}`);
}
walk(siteDir);
The idempotency logic: pages that already contain id="t-analytics" are skipped, so repeated local builds never produce duplicate injections.
4. Hook into the Build Pipeline
Edit package.json to append the injection step to postdocs:build:
"postdocs:build": "node scripts/copy-landing.mjs && node scripts/inject-analytics.mjs"
npm automatically runs postdocs:build after docs:build finishes. The build chain becomes:
docs:build(generates site/)
→ copy-landing(copies landing pages into the site root)
→ inject-analytics(injects tracking into all HTML)
5. Update the CI Trigger Paths
Edit .github/workflows/deploy-docs.yml to add scripts/inject-analytics.mjs to the push trigger paths, so changes to the injector trigger a redeploy:
on:
push:
branches: [main]
paths:
- 'website/**'
- 'scripts/copy-landing.mjs'
- 'scripts/inject-analytics.mjs'
- 'package.json'
- '.github/workflows/deploy-docs.yml'
website/analytics/**is already covered bywebsite/**, so it does not need a separate entry.
Verification
Local Build
npm run docs:build
Output similar to the following means the injection succeeded:
✅ analytics injected → site/index.html
✅ analytics injected → site/en/index.html
...
Open website/site/index.html and confirm there is a <script id="t-analytics" ...> before </head>, and that rebuilding does not double-inject.
After Deployment
- Push the code and wait for GitHub Actions to finish (about 2-5 minutes); confirm the deployment status is Active
- Open
https://zuorn.github.io/Tydora/and pressF12to open Developer Tools - Switch to the Network panel, refresh the page, and search for
umami— seeing requests tocloud.umami.is(e.g./api/send) means tracking is active - After a few visits, the Umami dashboard will show PV/UV, referrers, top pages, and more
FAQ
Q: Why can't I enter zuorn.github.io/Tydora in the Umami Domain field?
The Domain field only matches the host, not paths. Enter zuorn.github.io — the /Tydora part is ignored and does not affect tracking ownership:
- Umami only receives reports from pages that actually load the tracking script. Other GitHub Pages projects without the script never report, so data stays clean
- Page URLs in reports still include the full path (e.g.
/Tydora/en/...), so you can filter by path
Q: When does tracking start counting?
Only new visits after deployment are counted. Visitors who visited before the deploy and still have the old page cached will only be recorded after they refresh and load the new page; historical visits are not backfilled.
Q: Why can't I see requests in the Network panel?
Check two things:
- Whether the deployed page source contains
<script id="t-analytics" ...>before</head> - Whether the Domain in the Umami dashboard is
zuorn.github.io(without the path)
Q: How do I switch analytics services?
Edit only website/analytics/snippet.html and replace it with the new provider's script — the injection mechanism requires no changes.
Related Documents
- 09-blog/Auto-Update-Configuration — Auto update configuration
- 09-blog/Publish-to-Microsoft-Store — Publish to Microsoft Store