Track Outbound Link Clicks with CloudFlare
Track Outbound Link Clicks with CloudFlare

Track Outbound Link Clicks with CloudFlare

September 14, 2022
Clark Weckmann
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 a 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 of 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 for (let i = 0; i < links.length; i++) { links[i].href = '' + 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 (!'a[href^="http"]')) return; // Don't follow the link event.preventDefault(); // send request to window.location.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 for (let i = 0; i < links.length; i++) { links[i].href = '' + 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 that rate the entire process could be handled that way.
notion image

Configuring Firebase.

In your Firebase dashboard create a new project and provision a real-time 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 = ''; 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*, 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 the domain you used.

DNS Setup.

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

Test Your Worker Script.

Testing your Worker Script is as easy as it gets. Navigate to You should be redirected to SOMEWEBSITEHERE and if you check your RTDB you’ll notice it was updated 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 continued support keeps me motivated. I try my best to format these articles in a way anyone can understand.