# Hosting a Go Vanity Router with Cloudflare Pages

## Introduction

Are you tired of having to expose your brilliant and professional **Golang** projects under your repository hosting service domain? Would you like to tell your coworkers to simply `go get awesome.dev/amazing-project` (please don't try to run that, no idea where it might bring you...).  
In this short guide, I'll show you how I set up a simple router using **Cloudflare Pages** (and **Functions**).

## go get-ing stuff

Go doesn't offer any main registry like cargo, maven or npm. It operates slightly differently, and we're used to `go get` or `go install` the code from the repository directly. Therefore, any developer that hosts their projects on sites like GitHub or GitLab will have their modules imports be something like:

```bash
go get github.com/name-surname/package-name
```

Of course, this is a valid and hassle-free way of making your work available to the world, but it ends up a bit boring, doesn't it?

## The 'need' for a vanity router

A vanity router, in this case, would be a simple redirecting service that can route the `go get` request for a package to a custom URL. For example, with my router I translate all the requests coming to [go.lorenzomilicia.dev/](http://go.lorenzomilicia.dev/) to the corresponding GitHub repository under [github.com/lorenzo-milicia/](http://github.com/lorenzo-milicia/).

There is an actual real use case for the use of this rerouting mechanism, and it's the be independent of the git repository hosting service. You wouldn't want all of your users to have to change their import if you switched from GitHub to GitLab, right?

## Behind the scenes

### The `<meta>` tag

Implementing the rerouting logic is extremely simple. Whenever a `get`/`install` request is made, you're appending to a query parameter `go-get=1` . If the HTML that is served contains the meta tag

```html
<meta name="go-import"
  content="go.lorenzomilicia.dev/package git https://github.com/lorenzo-milicia/package">
```

then the server is telling the caller that the **go.lorenzomilicia.dev/package** is gettable, and specifies where it can be downloaded. Looking at the keys of the tag, we see the 'original' request and also the git repository to fetch the code.

### The router

Building the router becomes easy! All that is needed is a server capable of returning us with an HTML page with the appropriate `<head>` tag, based on the path the custom domain gets called with.

## Hosting the server

To serve this small service, I decided to use [**Cloudflare** **Pages**](https://developers.cloudflare.com/pages/get-started/) and its [**Functions**](https://developers.cloudflare.com/pages/platform/functions/), which are very similar to Cloudflare's Workers. I figured that *serverless* would fit this use case quite well: whenever someone calls the URL (in this case whenever someone `go get`s one of my packages), the Function is executed, and the correct HTML gets served. This can be all done with a Cloudflare free subscription, as long as you mind the limitation on the number of requests that can be handled under this plan.

I'm not a Javascript/Typescript developer, so I'll spare you the simple code I wrote for my vanity router. I followed Cloudflare's documentation to build Functions in typescript.

After cloning [my project](https://github.com/lorenzo-milicia/go-vanity-router), all you need is to use Clouflare's `wrangler` CLI tool to publish your Cloudflare Page. Again, I'll point you to the [exhaustive documentation.](https://developers.cloudflare.com/workers/wrangler/commands/#pages)

## How to use the vanity router

The logic behind the router relies on three external Environment Variables:

* **CUSTOM\_DOMAIN**
    
* **REPOSITORY\_URL**
    
* **CUSTOM\_ROUTINGS**
    

The first two are simply the routing you want to take effect, composed as this:

```bash
<meta name="go-import"
  content="$[{CSTOM_DOMAIN}package git https://${REPOSITORY_URL}/package">
```

> ⚠ The protocol https:// is added in front of the repository URL passed as an environmental variable, so my implementation only works with the secure protocol!

The third variable should contain a comma-separated list of the custom mappings that the router should take into consideration when creating the repository URL.  
For example, I want to keep all my library projects separate on GitHub, and I'll also most definitely write libraries with other languages. So I don't want to reserve a general *libs* prefix or directory name. But I would like the structure of my packages from the outside to keep a clean structure, so I want all my libraries to be gettable with the [go.lorenzomilicia.dev/libs/](http://go.lorenzomilicia.dev/libs/). By settings `CUSTOM_ROUTINGS="libs/,go-lib-"`, I'm telling the router that every request for a library `/libs/` should go look for a GitHub repository that starts with `go-lib`.  
Let's make it clearer with a table:

| Custom Routing? | Input | Output |
| --- | --- | --- |
| ❌ | go.lorenzomilicia.dev/package | github.com/lorenzo-milicia/package |
| ✅ libs/ -&gt; go-lib | go.lorenzomilicia.dev/libs/best-ever | github.com/lorenzo-milicia/go-lib-best-ever |

### v2 and beyond

As you might know, Go has a peculiar way of handling major versions once you move to a 2.0 and above. Most notably, Go expects the module to be suffixed with a `/v2` (or whatever major version the specific module is at). To correctly be able to route the requests to the repository that exposes a major version that requires this suffix, the path to the repository needs to be stripped from this.

The vanity router takes care of this, so the final outputs will be:

| Major ⩾ 2? | Input | Output |
| --- | --- | --- |
| ❌ | go.lorenzomilicia.dev/package | github.com/lorenzo-milicia/package |
| ✅ | go.lorenzomilicia.dev/package/v2 | github.com/lorenzo-milicia/package |
| ❌ | go.lorenzomilicia.dev/v4nity | github.com/lorenzo-milicia/v4nity |

## Conclusions

Now you have all you need to host your personal Vanity Router for your Go projects, and all your friends and colleagues will be impressed by your professionalism, which under the hood will be mostly free of charge (excluding the domain you might want to route your requests from).  
Check out the [repository](https://github.com/lorenzo-milicia/go-vanity-router), and upload the files to your own Cloudflare Page to get started!

## Links

[https://github.com/lorenzo-milicia/go-vanity-router](https://github.com/lorenzo-milicia/go-vanity-router)  
[https://developers.cloudflare.com/pages/get-started/](https://developers.cloudflare.com/pages/get-started/) [https://developers.cloudflare.com/workers/wrangler/commands/#pages](https://developers.cloudflare.com/workers/wrangler/commands/#pages) [https://developers.cloudflare.com/pages/platform/functions/](https://developers.cloudflare.com/pages/platform/functions/)
