Getting Accurate Analytics: Proxying Plausible with CloudFront and SST

Getting Accurate Analytics: Proxying Plausible with CloudFront and SST

Leverage AsyncLocalStorage to manage context provision and extend functionality without changing core code.

5 min read

If you’re running a website or web app, you know how crucial analytics are. There are several privacy-focused analytics tools out there (like Plausible Analytics and Fathom) that offer a great way to understand your visitors. I personally use Plausible Analytics and have been very happy with it.

But here’s a snag: many adblockers and privacy tools don’t just block ads; they often block requests to known analytics domains, including Plausible’s. This means the data you’re collecting might be incomplete, giving you a skewed picture of your traffic.

But here’s a snag: many adblockers and browser privacy features try to prevent data collection altogether by blocking requests to known analytics domains, including Plausible’s. This means that your analytics data might be incomplete.

So, how can we improve data accuracy? Thankfully, Plausible themselves recommend a robust solution: proxying the analytics requests through your own domain infrastructure.

In this post, we’ll briefly cover why this proxy approach is effective and then dive straight into the how. We’ll set up a reliable proxy using AWS CloudFront, defined and deployed easily using SST (Serverless Stack) as our Infrastructure as Code tool. Let’s configure our setup for more accurate analytics!

Why Proxy Plausible Analytics?

You’ve set up Plausible Analytics, hoping to get clear insights into your website’s traffic. But there’s that common hurdle: adblockers and browser privacy features. These tools often maintain blocklists containing domains associated with tracking and analytics – and plausible.io is frequently on those lists.

What happens then? When a visitor’s browser (with such a blocker) tries to send data to Plausible, the request gets intercepted and blocked before it even leaves their machine. The result? That visit simply isn’t recorded. This doesn’t just slightly lower your numbers; depending on your audience’s use of these blockers, it can lead to significantly underreported traffic and unreliable metrics. You might be missing out on understanding a large chunk of your user base.

ℹ️

Tech-savvy users are of course more likely to use adblockers, which makes them more likely to block analytics requests.

This potential for missing data is why Plausible officially recommends routing analytics requests through your own domain if you want more accurate data:

Are you concerned about missing data? Proxy our script. This is the option for those who want to get more accurate stats.

The key benefit of setting up a proxy (like we’re about to do with CloudFront) is changing the context of the analytics requests. The script on your site will send data not directly to plausible.io, but to a URL associated with your own domain (e.g., the CloudFront URL we’ll generate). From the perspective of browser blockers, these requests now look like standard first-party API calls (requests to the same domain the user is visiting), not calls to an external third-party tracking domain. Consequently, they are far less likely to be flagged and blocked.

In short, proxying turns Plausible requests into first-party requests, bypassing many blockers and giving you much more accurate and reliable analytics data. It’s a small infrastructure tweak for a significant improvement in data quality.

The Setup: Proxying with CloudFront and SST

Long story short, lets implement this with CloudFront and SST. There is an official guide from Plausible on how to do this in general with CloudFront but I want to show you how to do it with SST (since that’s what I’m using for this blog).

With SST, you can easily deploy various frontend frameworks (like Astro, React Router, Next.js, etc.). In my case, I’m using Astro. This is how the most basic Astro setup with SST looks like:

export const astro = new sst.aws.Astro('Astro', {
path: 'apps/www', // path to your Astro app
});

This already deploys a CloudFront distribution with a default behavior for you.

ℹ️

You can add a custom domain using the domain property. The details of that are out of scope of this post though.

Now, we need to create a new origin for plausible.io and add two behaviors to that distribution: one for the Plausible analytics script and one for the event API.

This is how the config for those look like (it is basically the codifcation of the configs from the official guide):

const plausibleOrigin = {
domainName: 'plausible.io',
originId: 'plausibleCustomOrigin',
customOriginConfig: {
httpPort: 80,
httpsPort: 443,
originSslProtocols: ['TLSv1.2'],
originProtocolPolicy: 'https-only',
},
} satisfies aws.types.input.cloudfront.DistributionOrigin;
const analyticsScriptBehavior = {
pathPattern: '/js/script.*',
targetOriginId: plausibleOrigin.originId,
viewerProtocolPolicy: 'https-only',
allowedMethods: ['GET', 'HEAD'],
cachedMethods: ['GET', 'HEAD'],
defaultTtl: 0,
maxTtl: 0,
compress: false,
forwardedValues: {
cookies: {
forward: 'none',
},
queryString: false,
},
} satisfies $util.UnwrappedArray<
$util.Input<aws.types.input.cloudfront.DistributionOrderedCacheBehavior>
>[number];
const eventApiBehavior = {
pathPattern: '/api/event',
targetOriginId: plausibleOrigin.originId,
viewerProtocolPolicy: 'https-only',
originRequestPolicyId: 'acba4595-bd28-49b8-b9fe-13317c0390fa',
cachePolicyId: '4135ea2d-6df8-44a3-9df3-4b5a84be39ad',
allowedMethods: ['GET', 'HEAD', 'OPTIONS', 'PUT', 'POST', 'PATCH', 'DELETE'],
// cachedMethods: ['GET', 'HEAD'], // Note: cachedMethods is ignored when using Cache Policies
} satisfies $util.UnwrappedArray<
$util.Input<aws.types.input.cloudfront.DistributionOrderedCacheBehavior>
>[number];

These are configurations for the required Pulumi aws.cloudfront.Distribution and aws.cloudfront.DistributionBehavior resources. SST is using Pulumi under the hood for the AWS infra.

The guide mentions to use the Managed-UserAgentRefererHeaders policy for the event API behavior. You can find the policyId by clicking on thein the AWS Console under CloudFront > Policies > Managed Policies > CachingDisabled.

CloudFront Policy ID

Also, note that the cachedMethods property is required but it is ignored when using Cache Policies.