When choosing software, I always consider privacy as a make-or-break factor. One tool that I needed recently for a project was a URL shortener. There are many openly available apps out there, but using them is not the best option for me for two main reasons: I can lose control over my data and get frustrated with their limitations.
The alternative is to build my own app. Thankfully, n8n offers the needed capabilities to rapidly build web applications, without going through long coding nights. In this post, I will show you how I built my self-hosted URL shortener that can be customized and extended to fulfill different use cases. The final n8n workflow looks like this:
The Idea
n8n hosts monthly community meetups, where members can present their workflows. At the April meetup, I was inspired by Jason's presentation of a newsletter sign-up workflow and his unique approach of using n8n as a full-fledged, highly extensible back-end. I decided to use that same approach to build a personal, self-hosted URL shortener that serves three functions:
- Creating the short URL
- Redirecting to the long URL
- Generating statistics on a dashboard
Requirements
This project was created using n8n and nginx docker images, so before starting, I needed Docker and Docker-Compose installed. Also, I used Airtable as my database, which requires an Airtable account. To follow this tutorial, you need to have some basic experience with n8n and know how to configure nodes and expressions. Now let's get started!
Quickstart
To have a re-deployable infrastructure, I relied on a docker-compose file, in which I specified the necessary information about the docker containers.
First, I created an nginx.conf
file to configure my nginx instance as a proxy. That way I could listen on port 80 of the localhost.
Second, I configured the default n8n docker image to enable basic authentication. Also, I initiated the N8N_HOST
and VUE_APP_URL_BASE_API
environment variables with my preferred short URL base: n8n.ly.
Third, I registered the short URL base into my local DNS, by appending the next line to the /etc/hosts
file:
Now I could reference the n8n.ly domain locally and get redirected to my n8n instance.
The Workflow
The URL shortener is powered by a workflow containing 29 nodes. The workflow is split into three lines:
1. Short URL creation line with 12 nodes
In this line, I used a combination of Webhook node, Set node, and IF node to validate the request, then a combination of Crypto node, Airtable node, and Set node to create the short URL and append it to the database. Finally, I used the Set node to return the result.
2. Redirection line with 12 nodes
In this line, I used a combination of Webhook node, Set node, and IF node to receive the request, then the Airtable node, IF node, and Set node to retrieve the long URL. Finally, I used a Set node to prepare the result.
3. Dashboard line with 5 nodes
In this line, I handled the request using a Webhook node and used the Airtable node to retrieve all records. Then I converted those records into embedded statistics using a combination of Function node and Set node.
Now let’s go through each workflow line.
1. Short URL creation
I started by building the short URL creation line. In it, I generated a unique ID for the provided long URL and saved a record containing the generated ID, the long URL, the short URL, the number of clicks, and the hostname to a database. This is what the workflow line looks like:
Initially, I configured my Webhook node to accept requests on http://n8n.ly/w/sh and to return the last JSON object as HTTP response.
Then, using the IF node, I checked the existence of the ?url
query parameter. If the URL existed (true branch), I used the Set node to extract it. Else (false branch), I set an error value using a Set node.
After extracting the URL from the Webhook node output, I relied on hash functions to generate its unique ID. To calculate the URL HASH value, I used the Crypto node to generate a SHA256 hash.
After calculating the HASH, I used a Set node to prepare the database record, which contains the Id, longUrl, and shortUrl fields.
After preparing the record, I verified if the generated ID already existed in my database. To do that, I used the Airtable node to retrieve the record by ID.
Then, I used an IF node to check whether the record already existed. If the record was found in the database (true branch), I used a Set node to set the shortUrl as the output. Else (false branch), I used a Set node to append a host and click fields to the prepared record.
Then I used the Airtable node to append the record to the database and another Set node to set the created shortUrl as the output.
2. Redirection
Now with the redirection line, I aimed to redirect the user's web browser to the right long URL. To do that, I needed to retrieve the record using the provided ID, update the number of clicks for later statistics, and return a redirection page. The second workflow line looks like this:
Initially, I configured the Webhook node to listen on http://n8n.ly/w/go and to return the first JSON entry:
Then I used the IF node to check the existence of the ?id
query parameter. If the id query field existed (true branch), I used a Set node to extract it. Else (false branch), I returned an error page by using a Set node to set the error message.
Now that I had extracted the id parameter, I could verify if it existed in the database. If the record was not found, I used a Set node to create the correspondent error message. If the record existed (true branch), I used a Set node to increment the clicks counter, then an Airtable node to update the record in the database.
After updating the database record, I used a Set node to prepare the HTML output. The secret here was embedding a piece of JavaScript code that would redirect the user to the long URL:
3. Dashboard
Now I have reached the most exciting part of the post: creating a dashboard. After designing my dashboard using Figma and implementing it using HTML and CSS, I could serve it to users. The workflow line for this step looks like this:
I started by configuring a Webhook node to handle requests for http://n8n.ly/w/dashboard and return the first JSON entry to the user. The configuration for the Webhook node is similar to the previous one.
Then, I used an Airtable node to retrieve all the records from my database. Once I got the database result, I applied some magic to convert the record into statistics by using one of the most powerful nodes in n8n: the Function node, with the following code:
items = items.map(item => item.json.fields);
const totalLinks = items.length;
const totalClick = items.map(item => item.clicks).reduce((acc,val) => acc+=val);
const hostsMap = new Map();
const hosts = items.map(item => item.host);
hosts.forEach(host => {
hostsMap.set(host,hostsMap.get(host)!==undefined?hostsMap.get(host)+1:1)
});
const totalHosts = [...hostsMap.keys()].length;
return [{
json:{
totalLinks,
totalClick,
totalHosts
}
}];
After calculating the total links, total clicks, and total hosts numbers, I used a Set node to embed those statistics in my dashboard code:
This is the full code in the expression:
Now I could finally access the dashboard at http://n8n.ly/w/dashboard, which displays the total number of clicks, link, and hosts:
Next steps
In this post, I showed you how I used n8n to build a highly extensible, self-hosted URL shortener. I relied on a few core nodes like Webhook, IF, Set, Crypto, and Function to build three workflow lines that combined allowed me to create an app for my purposes. You can find the workflow here, feel free to use it and let me know what you think.
The workflow can be extended and customized to:
- Use a different database node and make it 100% self-hosted
- Offer a customized short URL where the user provides a slug instead of using an ID
- Enrich the statistics returned in the dashboard to include more details
Start automating!
The best part is, you can start automating for free with n8n. The easiest way to get started is to download the desktop app, or sign up for a free n8n cloud trial. Thanks to n8n’s fair-code license, you can also self-host n8n for free.
Did you find this article useful or inspiring? Share it on Twitter or discuss it in the community forum 🧡