Hosting · 3 min setup

How to add analytics to a Cloudflare Pages site

To add VisitTrack to a Cloudflare Pages site, put the script tag in your site's HTML or framework layout — or inject it into every HTML response with a small functions/_middleware.js using HTMLRewriter. If Rocket Loader is on for the zone, add data-cfasync="false" before src so Cloudflare leaves the tag alone.

Updated

Cloudflare Pages has a feature most hosts don't: middleware that runs at the edge on every request. That gives you a no-template-edit install option, and it's also the right place to report AI crawlers for a static site, because the edge is the only layer that sees them. (Cloudflare also steers new projects toward Workers with static assets; the same tag-in-HTML approach applies there, and the AI crawler docs have a Workers snippet.)

Cloudflare Pages at a glance

Simplest
Tag in your HTML / framework layout
No-template option
functions/_middleware.js + HTMLRewriter
Rocket Loader
Add data-cfasync="false" before src
Previews
*.pages.dev dropped via Allowed hostnames
CSP
_headers file in your build output

How to install VisitTrack on Cloudflare Pages

  1. Step 1: Copy your site id

    Add your domain in VisitTrack and copy the snippet from the install step. Replace SITE_ID below.

  2. Step 2: Add the tag to your HTML (recommended)

    In your static index.html, base template or framework layout, inside <head>. The data-cfasync="false" attribute is only needed if Rocket Loader is enabled on your zone, and must come before src.

    <head>
    <script defer data-cfasync="false" data-site="SITE_ID" src="https://visitrack.app/tracker.js"></script>
  3. Step 3: Or: inject it with Pages Functions

    No access to templates (for example a pre-built export)? This middleware appends the tag to the <head> of every HTML response and passes everything else through unchanged.

    functions/_middleware.js
    // functions/_middleware.js — injects the tag into every HTML response
    export async function onRequest(context) {
      const response = await context.next();
      const type = response.headers.get("content-type") || "";
      if (!type.includes("text/html")) return response;
    
      return new HTMLRewriter()
        .on("head", {
          element(head) {
            head.append(
              '<script defer data-site="SITE_ID" src="https://visitrack.app/tracker.js"></script>',
              { html: true },
            );
          },
        })
        .transform(response);
    }
  4. Step 4: Keep preview deployments out

    Branch and preview deployments get *.pages.dev URLs. In VisitTrack, save your production domain under Settings → General → Allowed hostnames so those hits are dropped.

How to check VisitTrack is working on Cloudflare Pages

  1. Open the production domain and view source: the tag is in <head> (with the middleware option, view source shows the rewritten HTML).
  2. Browse two pages; VisitTrack's live view shows you within seconds.
  3. If the tag's type was rewritten (for example to text/rocketscript) in the delivered HTML, Rocket Loader is handling it — add data-cfasync="false".

Static pages count one pageview per load; framework sites with client-side routing (SvelteKit, Astro with view transitions, React Router, Nuxt) are followed through the History API. Cloudflare's caching doesn't affect counting — the script runs in the visitor's browser.

How to track custom events and goals in Cloudflare Pages

Use data-vt-goal on elements and window.visitrack("name") in your client code. For signups or payments handled in a Pages Function, POST /api/track with a secret API key stored as an encrypted environment variable in the Pages project settings (bound to context.env).

functions/api/signup.js
// functions/api/signup.js
export async function onRequestPost({ request, env }) {
  const { email, visitorId } = await request.json();
  // …create the account…
  await fetch("https://visitrack.app/api/track", {
    method: "POST",
    headers: { Authorization: `Bearer ${env.VISITRACK_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ type: "event", visitorId, name: "signup" }),
  });
  return Response.json({ ok: true });
}

Track AI crawlers on Cloudflare Pages

AI crawlers don't run JavaScript, so a static site only sees them at the edge. This middleware (from the AI crawler tracking docs) reports every request with waitUntil, so the crawler never waits. cf-connecting-ip is the genuine client address, so crawler verification works immediately. If you also use the injection middleware above, combine both into one _middleware.js.

Note that once a middleware runs on every path, requests for static assets become Functions invocations, which count toward your Workers request allowance. Use _routes.json to exclude asset folders if that matters for your traffic.

functions/_middleware.js
// functions/_middleware.js — from /docs/ai-crawler-tracking
export async function onRequest(context) {
  const { request, next, waitUntil } = context;
  const url = new URL(request.url);

  waitUntil(
    fetch("https://visitrack.app/api/collect-bot", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        siteId: "SITE_ID",
        path: url.pathname,
        userAgent: request.headers.get("user-agent"),
        ip: request.headers.get("cf-connecting-ip"),
      }),
    }).catch(() => {}),
  );

  return next();
}

Script options you might need on Cloudflare Pages

All optional — add them to the same tag. Full reference: script configuration.

AttributeWhat it does
data-cookielessStore nothing in the browser; the server derives a daily-rotating id. See cookieless mode.
data-exclude="/app/*"Never track these paths (comma-separated, * wildcard). Checked on every navigation.
data-allow-localTrack on localhost and *.local — for testing an install only.
data-debugLog every event the script sends, and why it skipped one, to the console.
data-auto="false"Turn off automatic events (downloads, contact clicks, form submits, rage clicks, JS errors, 404s).

Cloudflare Pages troubleshooting

The tag loads but nothing is recorded

Rocket Loader may be deferring or rewriting it. Add data-cfasync="false" before src, or turn Rocket Loader off for the zone.

*.pages.dev hosts in the Hostname tab

Save your production domain under Allowed hostnames in VisitTrack.

CSP blocks the script

Add the origin in your _headers file: /* then Content-Security-Policy: script-src 'self' https://visitrack.app; connect-src 'self' https://visitrack.app; (merged into your existing policy).

The middleware doesn't run

It must live at functions/_middleware.js in the project root (not inside the build output) and be deployed through Pages.

Cloudflare Pages analytics FAQ

How do I add a tracking script to Cloudflare Pages?

Add the script tag to your site's HTML or framework layout. Without template access, a functions/_middleware.js using HTMLRewriter can append it to every HTML response.

Does Cloudflare Rocket Loader break VisitTrack?

It can rewrite third-party scripts. Add data-cfasync="false" before the src attribute on the VisitTrack tag so Rocket Loader skips it.

How is VisitTrack different from Cloudflare Web Analytics?

Cloudflare Web Analytics is free and traffic-focused. VisitTrack adds custom events, goals, funnels, revenue attribution, session replays and AI-crawler reports. See VisitTrack vs Cloudflare Web Analytics.

Can I track AI crawlers on a static Cloudflare Pages site?

Yes, with the edge middleware above. It reports each request's path, user agent and IP to VisitTrack, which classifies GPTBot, ClaudeBot, PerplexityBot and others.

Do preview deployments count in VisitTrack?

Only until you save Allowed hostnames. After that, hits from *.pages.dev preview URLs are dropped before they're stored.

Keep going

Add VisitTrack to your Cloudflare Pages site

Cookie-free analytics with revenue attribution, live visitors, funnels and session replays. One script tag, every feature on every plan. 14 days free, no card required.

Not on Cloudflare Pages? See all 29 integration guides.