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

  1. 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)
  2. Click Add website:
    • Name: any name, e.g. Tydora
    • Domain: enter zuorn.github.iodo not include the /Tydora path (see FAQ below)
  3. 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 by website/**, 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

  1. Push the code and wait for GitHub Actions to finish (about 2-5 minutes); confirm the deployment status is Active
  2. Open https://zuorn.github.io/Tydora/ and press F12 to open Developer Tools
  3. Switch to the Network panel, refresh the page, and search for umami — seeing requests to cloud.umami.is (e.g. /api/send) means tracking is active
  4. 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:

  1. Whether the deployed page source contains <script id="t-analytics" ...> before </head>
  2. 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.