Golang Vanity URLs in nginx
Go has a nice way of importing packages, just use go get
and it automatically appears in your $GOPATH/src
directory. The backing store
can be any supported VCS, but most often is GitHub. This means that your custom
package will be namespaced under github.com/<user>/package
. Wouldn’t it be
nice if you could just namespace it similar to rsc.io/quote
?
The way go get
works is that it has special cases for handling Github,
Bitbucket and a few other VCS providers, but if it cannot match any of the known
providers, it attempts a HTTPS request to the given URL with ?go-get=1
appended to the request. The server recognizes this special request and responds
with a meta
tag telling Go where to find this. The tag is of the following
form:
<meta name="go-import" content="rsc.io/quote git https://github.com/rsc/quote.git">
The first part of the content (rsc.io/quote
) tells Go where to namespace this
package under $GOPATH/src
. The rest of the content is the VCS type, followed
by the URL to the actual source repository.
By allowing this model, the owner of the package can change the location of the source repository at any time, while still keeping the vanity import.
With nginx, this is actually super easy. I have namespaced all my Go modules
(few they may be) under nirenjan.org
. For example, if you run go get nirenjan.org/posix
, you will end up with the source code under
$GOPATH/src/nirenjan.org/posix
, rather than
$GOPATH/src/git.nirenjan.org/go/posix
.
location ~ /([A-Za-z0-9_-]+)(/[A-Za-z0-9_.-]+)*$ {
if ($args = "go-get=1") {
add_header Content-Type text/html
response 200 '<meta name="go-import" content="$host/$1 git https://git.nirenjan.org/go/$1.git">';
}
return 302 https://git.nirenjan.org/go/$1;
}
The overall idea is that all my packages would be at the root level under
nirenjan.org
, while any bump in the major versions would have the trailing
/vX
. The nginx config snippet above maps the root level to the actual Git
repository, and ignores any trailing suffix.
The only drawback is that it will match any URL, so even non-existent modules get mapped. That said, it doesn’t really matter, since this is a non-existent module, the clone step would fail.