---
title: "Plausible Analytics for Astro with Cloudflare Workers"
description: "Proxy Plausible Analytics through Cloudflare Workers on your Astro site. Free 100K requests/day setup that bypasses ad blockers for accurate, real visitor stats."
date: 2026-07-14
categories: ["web-development"]
tags: ["astro","plausible","cloudflare-workers"]
---

import { Picture } from "astro:assets";
import Notice from "@components/widgets/Notice.astro";
import ListCheck from "@components/widgets/ListCheck.astro";
import Tabs from "@components/widgets/Tabs.astro";
import Tab from "@components/widgets/Tab.astro";
import Accordion from "@components/widgets/Accordion.astro";
import Button from "@components/widgets/Button.astro";
import imag1 from "../../assets/images/23/12/create-worker.png";
import imag2 from "../../assets/images/23/12/deploy-worker.png";
import imag3 from "../../assets/images/23/12/cf-worker-edit-code.png";
import imag4 from "../../assets/images/23/12/worker-code.png";
import imag5 from "../../assets/images/23/12/worker-routes.png";

import YouTubeEmbed from "../../components/widgets/YouTubeEmbed.astro";

[Plausible Analytics](https://plausible.io/) is a privacy-first, open-source alternative to Google Analytics. It's lightweight (under 1KB script), uses no cookies, and tracks no personal data. With over 19,000 paying subscribers and 260 billion pageviews tracked, it's the most popular privacy-focused analytics tool among developers.

You can self-host Plausible on your own servers. Check [Install Plausible With One Click](https://www.bitdoze.com/install-plausible-analytics/) for a quick setup, or use one of the [best self-hosted server panels](https://www.bitdoze.com/best-self-hosted-panels/) for easy deployment.

[Astro](https://astro.build/) is a web framework built for speed, and it now runs natively on Cloudflare. After [Cloudflare acquired Astro in January 2026](https://blog.cloudflare.com/astro-joins-cloudflare/), the `@astrojs/cloudflare` adapter dropped Pages support entirely. v13+ deploys exclusively to Cloudflare Workers. You can [deploy your Astro site on Cloudflare](https://www.bitdoze.com/deploy-astrojs-cloudflare/) or [build a free blog with Astro & Cloudflare](https://www.bitdoze.com/build-astro-blog-free/) to get started. For a deeper look at framework choices, see the [Astro vs Next.js vs TanStack Start comparison](https://www.bitdoze.com/astro-vs-nextjs-vs-tanstack-start-which-wins/).

In this guide, you'll set up Plausible Analytics for your Astro site using Cloudflare Workers as a proxy. The result: accurate analytics that bypass ad blockers, served from your own domain, on a free plan.

<ListCheck>
  <ul>
    <li>Track real visitor data without ad-blocker interference</li>
    <li>Proxy Plausible scripts through your own domain via Cloudflare Workers</li>
    <li>Free setup (100,000 requests/day on Cloudflare's free plan)</li>
    <li>Works with Plausible Cloud or self-hosted Plausible Community Edition</li>
  </ul>
</ListCheck>

## Why proxy Plausible Analytics through Cloudflare Workers?

Ad blockers and privacy-focused browsers routinely block requests to `plausible.io`. This can cause you to lose 20-30% of your analytics data. If you're using tools like [NextDNS](https://go.bitdoze.com/nextdns) or browser-level ad blockers on your own devices, you already know how aggressively they block analytics scripts.



<YouTubeEmbed
  url="https://www.youtube.com/embed/EJtSVbqyXWU"
  label="Proxying Plausible Through Cloudflare Workers"
/>
A proxy solves this by routing Plausible requests through your own domain. To your visitors' browsers, the analytics script looks like part of your site, not a third-party tracker. Ad blockers can't distinguish it from regular site traffic.

<Notice type="info" title="Works with any static site">
This proxy approach works with any static site generator or framework, not just Astro. Hugo, Next.js, Eleventy, plain HTML. If you can add a script tag to your head, you can use this setup. The Cloudflare Worker handles the proxying regardless of your build tool.
</Notice>

For more on blocking ads and trackers at the DNS level, see [how to block ads and tracking with DNS protection](https://www.bitdoze.com/block-ads-malware-dns-protection/).

## Why use Cloudflare Workers instead of redirects?

If you're deploying to Cloudflare Pages, you might think you can use the `_redirects` file to proxy Plausible requests. You can't. Cloudflare Pages only supports relative-path redirects. It cannot proxy to external URLs.

Attempting to add a proxy redirect like this will fail:

```sh
Found invalid redirect lines:
09:59:12.187	  - #1: /js/script.js https://domain.com/js/plausible.outbound-links.js 200
09:59:12.187	    Proxy (200) redirects can only point to relative paths. Got https://domain.com/js/plausible.outbound-links.js
09:59:12.187	  - #2: /api/event https://domain.com/api/event 200
09:59:12.187	    Proxy (200) redirects can only point to relative paths. Got https://domain.com/api/event
```

Cloudflare Workers don't have this limitation. They can fetch from any external URL and return the response, making them the right tool for proxying Plausible on Cloudflare.

## Proxying Plausible through Cloudflare Workers (step by step)

Cloudflare Workers offers a free plan with 100,000 requests per day. That's more than enough for most sites. All you need is a free Cloudflare account.

<Notice type="success" title="Free tier is enough">
Cloudflare Workers free plan includes 100,000 requests/day. A typical site loading the Plausible script once per pageview will stay well within this limit.
</Notice>

### Step 1: Create a Worker

Go to the **Workers & Pages** section in your Cloudflare dashboard and click **Create application**. Then click **Create Worker** in the Workers tab. Give your worker a name that doesn't suggest analytics. Something like `theme-assets` or `cdn-helper` works well. Click **Deploy**.

**Create Cloudflare Worker:**

<Picture
  src={imag1}
  alt="Create Cloudflare Worker"
/>

**Deploy Cloudflare Worker:**

<Picture
  src={imag2}
  alt="Deploy Cloudflare Worker"
/>

### Step 2: Add the Worker code (ES Modules)

Click **Edit Code**, delete the default code, and replace it with the code below.

<Notice type="info" title="Service Worker syntax is deprecated">
The old `addEventListener('fetch', ...)` syntax is deprecated by Cloudflare. The code below uses the recommended ES Modules format. If you're updating from an older setup, replace your entire worker script.
</Notice>

<Tabs>
  <Tab name="Cloud Proxy (Plausible.io)">
    Use this version if you're on Plausible's hosted cloud service.

```js
// Replace 'pa-XXXXX.js' with your site-specific script ID
// Find it in Plausible: Site Settings → General → Data Snippets
const ProxyScript = 'https://plausible.io/js/pa-XXXXX.js';
const ScriptPath = '/theone/script';
const Endpoint = '/theone/event';

export default {
  async fetch(request, env, ctx) {
    ctx.passThroughOnException();
    const url = new URL(request.url);
    const pathname = url.pathname;

    if (pathname.startsWith(ScriptPath)) {
      return getScript(request, ctx);
    } else if (pathname === Endpoint) {
      return postData(request);
    }

    return new Response(null, { status: 404 });
  }
}

async function getScript(request, ctx) {
  let response = await caches.default.match(request);
  if (!response) {
    response = await fetch(ProxyScript);
    ctx.waitUntil(caches.default.put(request, response.clone()));
  }
  return response;
}

async function postData(request) {
  const req = new Request(request);
  req.headers.delete('cookie');
  return await fetch('https://plausible.io/api/event', req);
}
```

Replace `pa-XXXXX.js` with your actual site-specific script ID. You can find it in your Plausible dashboard under **Site Settings → General → Data Snippets**. The `theone` path segment can be anything you want. Just keep it consistent across `ScriptPath` and `Endpoint`.

  </Tab>
  <Tab name="Self-Hosted (Plausible CE)">
    Use this version if you're running Plausible Community Edition on your own server.

```js
// Replace with your self-hosted Plausible domain
const ProxyScript = 'https://plausible.yourdomain.com/js/pa-XXXXX.js';
const ScriptPath = '/theone/script';
const Endpoint = '/theone/event';

export default {
  async fetch(request, env, ctx) {
    ctx.passThroughOnException();
    const url = new URL(request.url);
    const pathname = url.pathname;

    if (pathname.startsWith(ScriptPath)) {
      return getScript(request, ctx);
    } else if (pathname === Endpoint) {
      return postData(request);
    }

    return new Response(null, { status: 404 });
  }
}

async function getScript(request, ctx) {
  let response = await caches.default.match(request);
  if (!response) {
    response = await fetch(ProxyScript);
    ctx.waitUntil(caches.default.put(request, response.clone()));
  }
  return response;
}

async function postData(request) {
  const req = new Request(request);
  req.headers.delete('cookie');
  return await fetch('https://plausible.yourdomain.com/api/event', req);
}
```

Replace `plausible.yourdomain.com` with your actual self-hosted Plausible domain, and `pa-XXXXX.js` with your site-specific script ID.

  </Tab>
</Tabs>

<Notice type="info" title="Performance optimization (optional)">
You can reduce proxy latency from ~140ms to ~9ms by returning an immediate 202 response and forwarding the event asynchronously. In the `postData` function, replace the final `return` with `ctx.waitUntil(fetch(...))` and return a `new Response('OK', { status: 202 })`. Note: some users have reported issues with request stream access after the response is sent, so test this carefully.
</Notice>

**Edit Worker:**

<Picture
  src={imag3}
  alt="Edit Cloudflare Worker"
/>

**Add Code:**

<Picture
  src={imag4}
  alt="Add Cloudflare Worker Code"
/>

Once you've added the code, click **Save and Deploy** in the top right.

### Step 3: Verify the Worker is working

Test your worker by accessing it directly:

```sh
https://your-worker-name.your-cloudflare-username.workers.dev/theone/script
```

You should see JavaScript code returned (the Plausible script). If you get a 404, double-check the `ScriptPath` variable in your worker code matches the URL path you're testing.

### Step 4: Run the proxy as a subdirectory

Running the proxy under a subdirectory of your main domain (e.g., `example.com/theone/`) is better than using a separate subdomain. It keeps requests first-party, which avoids cookie restrictions and looks cleaner in network logs.

To set this up, add a Worker route in the **Routes** section of your Worker settings:

**Route**: `*example.com/theone/*`

Replace `theone` with whatever path segment you chose in your worker code. Select your domain in the zone dropdown.

<Picture
  src={imag5}
  alt="Add Cloudflare Worker route"
/>

### Step 5: Add the Plausible snippet to your Astro site

<Notice type="warning" title="Snippet format changed">
The old `data-domain` / `data-api` format is deprecated. Plausible now uses a site-specific script (`pa-XXXXX.js`) with a `plausible.init()` call for the endpoint. Use the format below.
</Notice>

Add this snippet to your Astro site's `<head>`. In Astro, you can place it in your main layout file (e.g., `src/layouts/Layout.astro`):

```html
<script async src="https://yourdomain.com/theone/pa-XXXXX.js"></script>
<script>
  window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)};
  plausible.init=plausible.init||function(i){plausible.o=i||{}};
  plausible.init({
    endpoint: "https://yourdomain.com/theone/event"
  })
</script>
```

Replace the following:
- **`yourdomain.com`**: your actual site domain
- **`theone`**: the subdirectory path you configured in Steps 2 and 4
- **`pa-XXXXX.js`**: your site-specific script ID from Plausible (Site Settings → General → Data Snippets)

The `endpoint` URL must match the `Endpoint` variable in your Worker code, and the script `src` must match the `ScriptPath`. The script URL goes through your Worker (serving the cached Plausible script), and the endpoint URL routes analytics events through your Worker to Plausible's API.

You should now have Plausible Analytics proxied through Cloudflare Workers, served from your own domain.

## Plausible pricing (2025)

Plausible offers several cloud tiers. All plans include the core analytics features: pageviews, visitors, bounce rate, visit duration, referral sources, and more.

| Plan | Price | Pageviews | Sites | Notable Features |
|------|-------|-----------|-------|------------------|
| Starter | $9/mo | 10,000 | 1 | Core analytics |
| Growth | $14/mo | 10,000 | 3 | 3 team members |
| Business | $19/mo | 10,000 | 10 | Funnels, revenue goals, Stats API |
| Enterprise | Custom | Custom | Custom | SSO, Sites API, Managed Proxy |

Higher pageview tiers are available at each level. The **Enterprise** plan includes a [Managed Proxy](https://plausible.io/docs/proxy/guides/cloudflare) where Plausible handles the proxy for you via a CNAME record. No Worker needed.

If you want to skip the cloud pricing entirely, the Community Edition is free.

## Plausible Community Edition vs Cloud

Plausible Community Edition (CE) is a free, self-hosted version under the AGPL license. It follows an open-core model. CE includes the core analytics engine, but some advanced features are exclusive to the cloud Business and Enterprise plans.

<Tabs>
  <Tab name="Plausible Cloud">
    **Pros:**
    - Fully managed, no server to maintain
    - All features including funnels, revenue goals, and SSO
    - Managed Proxy option (Enterprise plan)
    - Automatic updates and security patches
    - Stats API V2 for custom integrations

    **Cons:**
    - Monthly cost starting at $9/mo
    - Data stored on Plausible's infrastructure
  </Tab>
  <Tab name="Plausible Community Edition">
    **Pros:**
    - Completely free, self-hosted
    - Full control over your data
    - AGPL licensed, open source
    - Core analytics features included

    **Cons:**
    - No funnels, revenue goals, or SSO
    - Self-managed server required
    - Manual updates and maintenance
    - No Managed Proxy option

    If you're self-hosting Plausible CE, affordable VPS providers like [Hetzner Cloud](https://go.bitdoze.com/hetzner) or [Hostinger VPS](https://go.bitdoze.com/hostinger-vps) offer solid performance for running your instance. See [Install Plausible With One Click](https://www.bitdoze.com/install-plausible-analytics/) for deployment instructions.
  </Tab>
</Tabs>

<Accordion label="Can I use this Cloudflare Worker proxy with Plausible CE?" group="faq" expanded="true">
Yes. The Worker code is the same. You just change the `ProxyScript` URL and the `postData` fetch URL to point to your self-hosted Plausible domain instead of `plausible.io`. Use the "Self-Hosted (Plausible CE)" tab in Step 2 for the correct code.
</Accordion>

## Troubleshooting common issues

<Accordion label="Analytics data not showing up" group="faq" expanded="true">
Open your browser's developer tools (Network tab) and reload your site. Look for requests to your subdirectory path (e.g., `/theone/script` and `/theone/event`). If the script request returns 200 but the event request fails, check that the `endpoint` URL in your snippet matches the Worker's `Endpoint` variable exactly. If neither request appears, verify your snippet is in the `<head>` and not blocked by Content-Security-Policy headers.
</Accordion>

<Accordion label="Worker returns 404" group="faq">
Verify the Worker route pattern matches your subdirectory. The route should be `*example.com/theone/*` (note the trailing `/*`). Also check that the `ScriptPath` and `Endpoint` variables in your Worker code match the paths in your snippet. Make sure the Worker is deployed (not just saved as a draft).
</Accordion>

<Accordion label="CORS errors in the console" group="faq">
Since the proxy runs on your own domain, requests should be same-origin and CORS shouldn't trigger. If you see CORS errors, your Worker route is likely misconfigured. The requests might be hitting Plausible directly instead of going through the Worker. Double-check the route pattern and ensure the snippet URLs point to your domain, not `plausible.io`.
</Accordion>

<Accordion label="Plausible dashboard shows 0 visitors" group="faq">
First, verify you're looking at the correct site in your Plausible dashboard. If you recently switched to the new snippet format, make sure the old `data-domain` snippet is completely removed. Having both can cause conflicts. Also check that your own browser isn't blocking the requests during testing (disable your ad blocker for your own domain).
</Accordion>

## What's new in Plausible since 2023

Plausible has shipped a lot of features since this article was first published. Here are the highlights:

<ListCheck>
  <ul>
    <li>Automatic scroll depth tracking (no setup required)</li>
    <li>AI Assistants traffic channel: tracks visits from ChatGPT, Claude, Gemini, Perplexity</li>
    <li>User Journeys: visualize how visitors navigate your site</li>
    <li>Automatic form submission tracking (toggle-on)</li>
    <li>Revenue goals and ecommerce attribution (Business plan)</li>
    <li>Funnels for multi-step conversion analysis (Business plan)</li>
    <li>Stats API V2 with simpler querying and multi-dimension support</li>
    <li>Search Console integration: keyword data in your dashboard</li>
    <li>Google Analytics import (both UA and GA4)</li>
    <li>Traffic drop alerts via email or Slack</li>
    <li>2FA and SSO security enhancements</li>
    <li>Improved "Time on Page" metric based on engagement signals</li>
    <li>Site-specific script format (`pa-XXXXX.js`) replacing the generic `script.js`</li>
  </ul>
</ListCheck>

If you're also working on your Astro site's performance, check [how to optimize Astro build speeds](https://www.bitdoze.com/astro-ssg-build-optimization/) or [migrate Astro to Bun on Cloudflare](https://www.bitdoze.com/migrate-astro-bun/) for faster builds.

<Button text="View Full Plausible Changelog" link="https://plausible.io/changelog" variant="outline" color="blue" size="md" />

## Frequently asked questions

<Accordion label="Does this work with frameworks other than Astro?" group="faq" expanded="true">
Yes. The Cloudflare Worker proxy is completely framework-agnostic. It works with any static site generator (Hugo, Eleventy, Next.js, Gatsby) or even plain HTML. The only requirement is that you can add a script tag to your site's `<head>`. The Worker handles all the proxying regardless of what generated the HTML.
</Accordion>

<Accordion label="What is Plausible's Managed Proxy?" group="faq">
Plausible's Managed Proxy is an Enterprise-only feature. Instead of setting up and maintaining your own Cloudflare Worker, you add a CNAME record pointing to Plausible's proxy infrastructure. They handle the rest. It's a good option for teams that want the proxy benefit without managing Workers, but it requires an Enterprise plan.
</Accordion>

<Accordion label="How much traffic can the free Workers plan handle?" group="faq">
Cloudflare Workers free plan allows 100,000 requests per day. Since the Plausible script is cached by the Worker, most pageviews only count as one request (the event POST). For a site with average traffic, 100K requests translates to roughly 80,000 to 100,000 pageviews per day. That covers the vast majority of sites. If you need more, Cloudflare's paid plan ($5/mo) includes 10 million requests per month.
</Accordion>

<Accordion label="Can I use Cloudflare Zaraz instead?" group="faq">
Cloudflare Zaraz is a tag manager that loads third-party scripts through Cloudflare's infrastructure. As of now, Plausible is not officially supported as a Zaraz integration. The Worker proxy approach described in this article remains the recommended way to proxy Plausible on Cloudflare. If Zaraz adds Plausible support in the future, it could simplify the setup.
</Accordion>