Effortlessly manage branded short links with seamless Google Tag Manager integration (via the noscript
feature), backed by the Cloudflare network.
To use this link shortener, here are some steps to follow:
- Run
npm install
inside the project directory. - Rename the
wrangler-sample.toml
file towrangler.toml
. - Read these instructions to customize the link shortener.
- Run
npm run authorize
to authorize your Cloudflare connection. - Finally, run
npm run deploy
to deploy your changes.
Here is an example of how the wrangler.toml
file for this link shortener should be configured:
name = "branded-short-links"
main = "src/index.ts"
compatibility_date = "2025-01-21"
############
## Routes ##
############
routes = [
{ pattern = "examp.le", custom_domain = true },
{ pattern = "www.examp.le", custom_domain = true },
]
#################
## Vars: Links ##
#################
[vars.links]
fallback_url = "https://www.example.com"
items = [
{ shortcode = "/url-1", http_response = 301, redirect_url = "https://www.new-site.com/url-1" },
{ shortcode = "/url-2", http_response = 302, redirect_url = "https://www.new-site.com/url-2" },
{ shortcode = "/url-3", http_response = 303, redirect_url = "https://www.new-site.com/url-3" },
{ shortcode = "/url-4", http_response = 307, redirect_url = "https://www.new-site.com/url-4" },
{ shortcode = "/url-5", http_response = 308, redirect_url = "https://www.new-site.com/url-5" },
]
####################
## Vars: Settings ##
####################
[vars.settings]
debug_mode = true
force_https = true
gtm_container_id = ""
With each request, the link manager will gather user data retrieved from the request
object (Cloudflare properties, headers, etc.). However, the extent of data collection will vary depending on the limitations imposed by which third-party analytics service you choose to use.
The variables supported are listed below:
Variable Name | Path | Description |
---|---|---|
bsl_redirectUrl |
The URL to redirect to when the link shortener runs. | |
bsl_shortcode |
The matched shortcode when the link shortener runs. | |
cf_asn |
request.cf.asn |
ASN of the incoming request, for example, 395747 . |
cf_asOrganization |
request.cf.asOrganization |
The organization which owns the ASN of the incoming request, for example, Google Cloud . |
cf_city |
request.cf.city |
City of the incoming request, for example, "Austin" . |
cf_colo |
request.cf.colo |
The three-letter IATA airport code of the data center that the request hit, for example, "DFW" . |
cf_continent |
request.cf.continent |
Continent of the incoming request, for example, "NA" . |
cf_country |
request.cf.country |
Country of the incoming request. The two-letter country code in the request. This is the same value as that provided in the CF-IPCountry header, for example, "US" . |
cf_isEUCountry |
request.cf.isEUCountry |
If the country of the incoming request is in the EU, this will return "1" . Otherwise, this property will be omitted. |
cf_latitude |
request.cf.latitude |
Latitude of the incoming request, for example, "30.27130" . |
cf_longitude |
request.cf.longitude |
Longitude of the incoming request, for example, "-97.74260" . |
cf_metroCode |
request.cf.metroCode |
Metro code (DMA) of the incoming request, for example, "635" . |
cf_postalCode |
request.cf.postalCode |
Postal code of the incoming request, for example, "78701" . |
cf_region |
request.cf.region |
If known, the ISO 3166-2 name for the first level region associated with the IP address of the incoming request, for example, "Texas" . |
cf_regionCode |
request.cf.regionCode |
If known, the ISO 3166-2 code for the first-level region associated with the IP address of the incoming request, for example, "TX" . |
cf_timezone |
request.cf.timezone |
Timezone of the incoming request, for example, "America/Chicago" . |
headers_cfConnectingIp |
request.headers |
The CF-Connecting-IP request header. |
headers_cfIpCountry |
request.headers |
The CF-IPCountry request header. |
headers_cfRay |
request.headers |
The CF-RAY request header. |
headers_host |
request.headers |
The Host request header. |
headers_userAgent |
request.headers |
The User-Agent request header. |
headers_xRealIp |
request.headers |
The X-Real-IP request header. |
request_method |
request.method |
The Request: method property. |
request_url |
request.url |
The Request: url property. |
In the world of many analytical tools, a Tag Management System (TMS) like Google Tag Manager makes it straightforward to manage digital marketing deployments and to consolidate multiple tracker sources. However, these systems typically rely on embedding JavaScript directly into users' browsers.
This approach poses a challenge for link shortening tools, for which the aim is to redirect users via a pre-configured "short link" without any client-side code. Moreover, modern browsers receiving a 3xx redirection response will not load the response body, which means analytical scripts will not load as well.
To address this, we can leverage the under-documented noscript
feature in Google Tag Manager, and have the link shortener execute the tags with the available information given in each request.
This approach will satisfy four different goals:
- Your environment would essentially be serverless, open-source, and backed by Cloudflare network.
- Redirects do not go through the
http-equiv
meta header, which is considered bad for SEO. - No additional maintenance required. Shortcodes not found would simply fall back to the original domain for further resolution.
- Developers would not need to re-configure and re-deploy the link shortener just to add a new tag.
To condense lengthy links into shorter links, configure each short link using the following settings:
shortcode
- A unique shortcode or alias. For instance, in
https://examp.le/url-1
,/url-1
serves as the shortcode.
- A unique shortcode or alias. For instance, in
http_response
- The desired HTTP response code —
301
,302
,303
,307
, or308
. You may read the Dr. Link Check blog post for more information.
- The desired HTTP response code —
redirect_url
- The destination URL to redirect to.
Below is an example demonstrating the configuration:
[vars.links]
fallback_url = "https://www.example.com"
items = [
{ shortcode = "/url-1", http_response = 301, redirect_url = "https://www.new-site.com/url-1" },
{ shortcode = "/url-2", http_response = 302, redirect_url = "https://www.new-site.com/url-2" },
{ shortcode = "/url-3", http_response = 303, redirect_url = "https://www.new-site.com/url-3" },
{ shortcode = "/url-4", http_response = 307, redirect_url = "https://www.new-site.com/url-4" },
{ shortcode = "/url-5", http_response = 308, redirect_url = "https://www.new-site.com/url-5" },
]
Below is an example demonstrating how will redirects work if url-1
is defined like the configuration above:
User Request | Matching Shortcode | Redirect URL |
---|---|---|
https://examp.le/url-1 |
/url-1 |
https://www.new-site.com/url-1 |
https://examp.le/url-1?abc=123#somehash |
/url-1 |
https://www.new-site.com/url-1 |
https://examp.le/url-1/?abc=123#somehash |
Fallback URL |
Note: If a request does not match any configured shortcodes, the request will default to the fallback_url
while retaining the original request.
The link shortener provides additional settings for your convenience. Here's what you can configure:
- To enable debug mode, set the
debug_mode
setting totrue
. - To enforce HTTPS, set the
force_https
setting totrue
. - To enable Google Tag Manager support, set the
gtm_container_id
to a non-empty value. Optional.
Note: If debug_mode
is enabled and the gtm_container_id
is defined, all custom image tags will run and their responses will be displayed instead of redirecting the user. Please exercise caution as enabling debug mode may reveal information typically concealed (e.g. API Secrets and Access Tokens).
When setting up the container for the first time, follow these steps to integrate the variables sent by the link shortener:
For each variable, use the following settings:
- Name: Based on the "Variable Name" column provided in the Supported Data section
- Variable Type: Data Layer Variable
- Data Layer Variable Name: Same as the "Name" setting above
- Data Layer Version: Version 2
- Set Default Value: Checked
- Default Value: N/A
For each trigger, use the following settings:
- Trigger Type: Page View
- This trigger fires on: Some Page Views
- To trigger tags if the user was redirected to the shortcode URL:
- Left box: Select the
bsl_shortcode
variable. - Center box: Select does not equal.
- Right box: Type N/A.
- Left box: Select the
- To trigger tags if the user was redirected to the fallback URL:
- Left box: Select the
bsl_shortcode
variable. - Center box: Select equals to.
- Right box: Type N/A.
- Left box: Select the
- To trigger tags if the user was redirected to the shortcode URL:
Note: For enhanced confidentiality, create a dedicated container exclusively for this link shortener and avoid sharing it with other domains.
A tool to generate tracking links is provided through this included tool. Pre-fill buttons are included for popular tracking solutions.
- [Google Analytics 4] To retrieve the API Secret and Measurement ID, view the query parameters documentation.
- [ntfy Server] If authentication is required, generate the
auth
parameter using the query param instructions.
When you first set up the link shortener, you may notice a large amount of unwanted traffic attempting to find exploits. Here's how we handled it with a single WAF rule:
- Select "Known Bots" from the "Field" dropdown and set it to "enable".
- Select "Threat Score" from the "Field" dropdown and set it to "greater than or equal to" and "5".
- Select "URI Path" from the "Field" dropdown and set it to "does not equal" and "
/url-1
".
Finally, set the action to Managed Challenge.
When deploying, there are a few things you need to be aware of:
- A domain name is required.
- You can conveniently register domains within Cloudflare at cost, without markup fees as seen with other domain registrars.
- Routes are not supported (when
custom_domain
is set tofalse
).- This is because the link shortener matches the beginning of the path without the forward slash (e.g.
examp.le/url-1
will matchurl-1
) defined in the link items in yourwrangler.toml
file.
- This is because the link shortener matches the beginning of the path without the forward slash (e.g.
If you find value in the ongoing development of this url shortener and wish to express your appreciation, you have the option to become our supporter on GitHub Sponsors or make a one-time donation through PayPal.