punchline!

Install

Install with a Cloudflare Worker

See how much of your site's traffic is AI agents, and which ones, even if a site builder like Webflow, Framer or Shopify hosts your site. A small Cloudflare Worker sits in front of your site and passes every request through unchanged. For each page load, it sends a privacy-reduced event to Punchline Analytics after the response has gone out.

  • Nothing changes for your visitors. No cookies, no script on your pages, and responses pass through untouched.
  • It sees what Google Analytics can't: crawlers, AI fetchers and coding agents that never run JavaScript.
  • Never breaks your site. If Punchline is misconfigured or unreachable, requests continue normally.

The Worker can also serve AI agents markdown when you turn that on in Site settings: same pages, easier for assistants to read. Until then it changes nothing.

Will it work with my site?

It works if your domain can run through Cloudflare's proxy (the orange cloud in Cloudflare's DNS settings):

Platform Works? Notes
Webflow Yes Supported by Webflow. Follow Webflow's Cloudflare guide for the DNS records
Framer Yes Follow Framer's "proxy with Cloudflare" guide
Shopify Yes, with a caveat Works through Cloudflare's Orange-to-Orange mode. Shopify's domain settings may show a "Cloudflare proxy" warning even though the store works
WordPress, Ghost, most hosts Yes Any origin you can put behind Cloudflare
Squarespace, Wix No They don't allow proxying. Use the browser snippet instead

Install from the Cloudflare dashboard (no code tools needed)

You'll need:

  • your site on Cloudflare (the free plan is fine)
  • your PUNCHLINE_DSN value (we send it when your site is set up)

1. Put your domain on Cloudflare and proxy it. If your domain isn't on Cloudflare yet, add it (Cloudflare walks you through changing nameservers at your registrar). Then follow your platform's guide above, so the record for your site, usually www, is Proxied (orange cloud).

2. Create the Worker. In Cloudflare, go to Workers & Pages → Create → Create Worker. Name it punchline and click Deploy. Then click Edit code, replace everything with the contents of punchline-worker.js, and click Deploy again.

3. Add your DSN as a secret. Go to the Worker's Settings → Variables and secrets → Add. Choose type Secret, name PUNCHLINE_DSN, value as sent to you. Click Deploy.

4. Route your site through the Worker. Go to the Worker's Settings → Domains & Routes → Add → Route, choose your domain, and enter the route www.example.com/* (use your own hostname). Set Failure mode to Fail open (proceed). Then if the Worker ever hits Cloudflare's free-plan limit, visitors still reach your site; only tracking pauses.

5. Check it's working. Visit a few pages of your site. They should appear in your Punchline dashboard within about a minute.

Free-plan limits

Cloudflare's free Workers plan includes 100,000 requests a day, and every request to the routed hostname counts, including images and scripts served from it. Most builders (Webflow, Shopify) serve assets from their own CDN domains, so usually only page loads count. If you get close to the limit, Cloudflare's Workers Paid plan raises it a lot. With "Fail open", hitting the limit never takes your site down.

Install with wrangler (developers)

npm install @punchline-analytics/cloudflare
// src/index.ts
import { punchline } from "@punchline-analytics/cloudflare";

export default punchline();
// wrangler.jsonc
{
  "name": "punchline",
  "main": "src/index.ts",
  "compatibility_date": "2026-09-01",
  "routes": [{ "pattern": "www.example.com/*", "zone_name": "example.com" }]
}
npx wrangler secret put PUNCHLINE_DSN
npx wrangler deploy

Already have a Worker? Wrap its handler instead:

import { withPunchline } from "@punchline-analytics/cloudflare";

export default withPunchline(async (request, env, ctx) => {
  // …your existing Worker…
  return fetch(request);
});

What gets sent

For each page load (GET/HEAD for pages, plus .txt, .md and .xml files such as robots.txt and llms.txt), one event is sent, server to server:

  • The host, the path and utm_* values. Every other query parameter, and the #fragment, are removed before sending.
  • The referrer's host only, the Accept header (cut to 200 characters), and whether the browser sent Sec-GPC: 1.
  • Cloudflare's country code for the request, and Cloudflare's verified-bot category when it gives one.
  • Web Bot Auth signature headers, when an agent sends them.
  • The response's status code and size (from Content-Length), sent once the response is known. This is what lets the Agent content report show whether agents actually got your pages.
  • The visitor's IP address and user agent. The Punchline collector uses these in memory to classify the request and to compute a daily visitor hash, then discards them; they're never stored or logged.

Not tracked: images, scripts, styles, other assets, POST requests, or in-page navigations and prefetches.

Options

punchline({
  dsn: "...",            // default: env.PUNCHLINE_DSN
  timeoutMs: 3000,       // give up sending after this long
  serverTiming: true,    // add the overhead to Server-Timing: `punchline;dur=…` (default: env.PUNCHLINE_SERVER_TIMING=1)
  onError: (err) => {},  // configuration or network errors (default: ignored)
});

Ask the docs

Answers come from these docs only, written by Claude (Anthropic). Your question is sent to answer it, and isn't stored.