Back

How to Build Your Own URL Shortener Service Using Cloudflare Workers

This post demonstrates how to implement a URL shortening service on Cloudflare using JS.

Table of contents

TL;DR / Geek Summary:

  • Serverless Hack: Implementing a DIY URL shortener using Cloudflare Workers and KV storage.
  • Logic Layer: JavaScript-based request handler with custom path mapping, 404 handling, and random ID generation.
  • OpSec Warning: Public deployments MUST disable open registration to prevent “dirty link” injection and domain blacklisting.

# How Build Your Own URL Shortener Service Using Cloudflare Workers

A short link, also known as a short URL or short code, refers to a web address that is visually compact. It works by shortening a standard, lengthy URL into a new, much shorter one, making it easier to share and distribute.

The main application scenarios for short links are as follows:

  • SMS Sending: Using short links in SMS messages can greatly reduce the character count. Nowadays, many marketing text messages utilize short URLs.
  • Community Promotion: Many community platforms or social networking sites block long links. Due to character limits on microblogs (like Weibo) or keyword link restrictions on platforms like WeChat Official Accounts, short URLs can reduce characters and bypass these restrictions.
  • WeChat Anti-Blocking: WeChat has various blocking mechanisms in place. Using short links helps avoid exposing the keywords of the original address, effectively bypassing the block.
  • Dynamic QR Codes (Live Codes): The short URL is fixed, but you can change the destination address by modifying the original link mapped to it in the backend. You don’t need to change the fixed short URL itself, making it act as a middle layer. This is mainly used in situations where the cost of replacing a link is high, such as with pre-generated/printed QR codes.

# Setup

This utilizes the service provided by Cloudflare Workers. The free tier allows up to 100,000 requests per day, which is more than enough for personal use.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
const html404 = `<!DOCTYPE html>
<body>
  <h1>404 0.</h1>
  <p>0.</p>
</body>`

// const statichtml = "https://raw.githubusercontent.com/1x000/zdzy/main/duanlnk/index.html"


async function randomString (len) {
    len = len || 6
    let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'    /****By default, confusing characters like oOLl, 9gq, Vv, Uu, I1 are removed****/
    let maxPos = $chars.length
    let result = ''
    for (i = 0; i < len; i++) {
        result += $chars.charAt(Math.floor(Math.random() * maxPos))
    }
    return result
}
async function checkURL (URL) {
    let str = URL
    let Expression = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/
    let objExp = new RegExp(Expression)
    return objExp.test(str) && str[0] === 'h'
}
async function save_url (URL, shortStr) {
    console.log("shortStr:", shortStr)
    let random_key
    if (!shortStr) {
        random_key = await randomString()
    } else {
        random_key = shortStr
    }
    let is_exist = await LINKS.get(random_key)
    console.log(is_exist)
    if (is_exist == null) {
        // Normal, put it in directly
        let stat = await LINKS.put(random_key, URL)
        if (typeof (stat) === "undefined") return random_key
        else return stat
    } else if (!shortStr) {
        // The generated random_key is a duplicate, call recursively
        return save_url(URL, null)
    } else
        // The custom path already exists
        return -1

}
async function handleRequest (request) {
    console.log(request)
    if (request.method === "POST") {
        let req = await request.json()
        console.log(req["url"])
        if (!await checkURL(req["url"]))
            return new Response(`{"msg":"0"}`, { status: 400, headers: { "Content-Type": "application/json" } })
        let random_key = await save_url(req["url"], req["shortStr"])
        console.log(random_key)
        // Successfully saved
        if (Object.prototype.toString.call(random_key) === "[object String]")
            return new Response(`{"data":{"shortUrl":"${random_key}"}}`, { status: 200, headers: { "Content-Type": "application/json" } })
        // Custom path is duplicated
        else if (random_key === -1)
            return new Response(`{"msg":"0"}`, { status: 400, headers: { "Content-Type": "application/json" } })
        // Didn't test what happens when KV is full, assuming it returns an error on put (lazy guess)
        else return new Response(`{"msg":"0"}`, { status: 500, headers: { "Content-Type": "application/json" } })
    }
    const requestURL = new URL(request.url)
    // todo Normalize the '/' in path
    const path = requestURL.pathname.toString().substring(1);
    console.log(path)
    if (!path) {

        const html = await fetch(statichtml)

        return new Response(await html.text(), {
            headers: {
                "content-type": "text/html;charset=UTF-8",
            },
        })
    }
    const value = await LINKS.get(path)
    console.log(value)


    const location = value
    if (location) {
        return Response.redirect(location, 302)

    }
    // If request not in kv, return 404
    return new Response(html404, {
        headers: {
            "content-type": "text/html;charset=UTF-8",
        },
        status: 404
    })
}



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

# Explanation

This code is a script used to create a URL shortening service utilizing Cloudflare Workers and KV storage. It allows users to input a long URL and receive a short URL that redirects to the original address. It also supports custom paths for short URLs. Here is a tutorial on how to use this code:

  1. Create a Cloudflare account and set up a Workers subscription. You can use the free plan, which allows up to 100,000 requests per day.

  2. In the Workers dashboard, create a KV namespace and name it LINKS. This namespace will be used to store the mapping relationship between short URLs and long URLs.

  3. Create a Worker in the Workers dashboard and paste this code into the editor. You can modify some variables in the code, such as html404 and statichtml, to customize the content of your 404 page and homepage.

  4. Deploy the Worker and assign it a domain name, such as example.workers.dev. This domain will serve as the base address for your URL shortening service.

  5. Visit your domain, and you should see a simple webpage featuring an input box and a button. You can paste a long URL into the input box, click the button, and you will get a short URL. You can also enter a custom path in the secondary input box (if provided by your static html) to specify the exact short URL you want. If the custom path is already taken, you will receive an error prompt.

  6. You can copy the generated short URL and open it in a browser—it will seamlessly redirect to the original long URL. You can easily share this short URL with others to direct them to your intended webpage.

# Warning

If you intend to use this publicly, you must remove the comment slashes from the statichtml variable line. If you want to keep the service private for yourself, please leave it commented out! There are always malicious actors who will inject illegal links into public shorteners, which can result in your domain getting blocked or penalized.