Track Outbound Link Clicks with CloudFlare

date
Sep 14, 2022
slug
track-outbound-link-clicks
status
Published
tags
Serverless
Cloudflare
type
Post
OG_override
summary
In this article you’ll learn how to track your outbound link clicks like all the major companies do, and we’ll do it for free. Google analytics is great, but sometimes it’s just too much. So I started using this home brew solution.
If you’re unfamiliar with CloudFlare workers I recommend reading my article on Using a Custom Domain with Notion for Free. It covers a more detailed process of CloudFlare workers. You’ll also have the added benefit of free custom domain for Notion at the end of it.

The Setup.

Before we even start with CloudFlare let’s find a way to target all your links that are not internal. There are a couple ways we can do this. Take a look at some of the examples below.
 
Getting all <a> tags that have an href' value of http*
//  get all a elements with an href attribute that starts with http
let links = document.querySelectorAll('a[href^="http"]');
//  loop through all links and prefix with https://out.mydomain.com/?link=
for (let i = 0; i < links.length; i++) {
    links[i].href = 'https://out.mydomain.com/?link=' + links[i].href;
}
Using event listeners and redirecting outbound links.
// add event listener to all links and prevent default action
document.addEventListener('click', function (event) {
    // If the clicked element doesn't have the right selector, bail
    if (!event.target.matches('a[href^="http"]')) return;
    // Don't follow the link
    event.preventDefault();
    // send request to out.mydomain.com
    window.location.href = 'https://out.mydomain.com/?link=' + event.target.href;
}, false);
Only select <a> elements you tag with .outbound.
// get all a elements with the class outbound
let links = document.querySelectorAll('a.outbound');
// loop through all links and prefix with https://out.mydomain.com/?link=
for (let i = 0; i < links.length; i++) {
    links[i].href = 'https://out.mydomain.com/?link=' + links[i].href;
}
There are several other ways to handle this that are far better. Personally I would pass this to the server side and anytime a link is clicked make a call to the outbound tracker, but a that rate the entire process could handled that way.
notion image

Configuring Firebase.

In your Firebase dashboard create a new project and provision a realtime database (RTDB).
For the purposes of this article we will leave your Firebase rules open. I highly recommend configuring authentication with a JWT token for your worker or even a simple API key.
Add some data to your RTDB, for the sake of this article create out_links.
We’ll be tracking the URL that was clicked and how many times it has been clicked with our CloudFlare Worker.

The Worker Script.

Visit your CloudFlare dashboard and create a new Worker.
Paste the code below for your Worker script.
// your RTDB url. out_links is the dataset you create in your RTDB
const firebaseUrl = 'https://PROJECTID.firebaseio.com/out_links/';
const statusCode = 301; // status code - type of redirect

async function handleRequest(request) {
    const { searchParams } = new URL(request.url);
    const url = searchParams.get('link');// geting the link to redirect to and track
    if (!url) {
        return new Response('No link provided', { status: 400 });
    }
    // update click count in firebase for url
		// format url storage
    const key = url.replace(/https?:\/\//, '').replace(/\//g, '_').replace(/\./g, '_');
    //get current clicks count from firebase
    const response = await fetch(firebaseUrl + key + '/clicks.json');
    const clicks = await response.json();
    //update clicks count in firebase
    await fetch(firebaseUrl + key + '/clicks.json', {
        method: 'PUT',
        body: JSON.stringify(clicks + 1)
    });
    //redirect to url
    return Response.redirect(url, statusCode);
}

addEventListener('fetch', async event => {
  event.respondWith(handleRequest(event.request));
});

Triggering Your Worker Script.

Within the Worker dashboard create a new trigger for your domain.
notion image
In this case mine is out.clark.today/*, the asterisk is important so we can listen on all paths.
There’s one more step to bring your Worker online with your custom route. Go to your DNS dashboard for your the domain you used.

DNS Setup.

Create a new proxied A record and point it to 192.0.2.0.
notion image
 

Test Your Worker Script.

Testing your Worker Script is as easy as it get. Navigate to yourdomain.com/?url=SOMEWEBSITEHERE. You should be redirected to SOMEWEBSITEHERE and if you check your RTDB you’ll notice it was update appropriately!
 

Thanks For Reading!

I want to say thank you to the people that continue to read my articles. I write these articles because I personally have either had a hard time getting something to work or I found something I think could benefit others. Your continues support keeps me motivated. I try my best to format these articles in a way anyone can understand.

© Clark Weckmann 2014 - 2022