surl

Short, durable links for long web addresses

GitHub

https://s.example.org/k7mq3xw

A tiny, self-hostable URL shortener. One binary, a handful of environment variables, one SQLite file. No database server, no container stack, no settings panel: about 5,000 lines of Go with four dependencies.

Get it on GitHub Read the README

The surl register: a three-step form to create a link, with the short link previewed at the size of a sign, above a searchable list of existing links with their visit counts.
The register: create a link in three steps, then search and sort what exists. It follows the operating system's dark mode.

What stands out

01

Signs in against Active Directory

Every link says who created it. Sign-in goes to your directory over ldaps:// only; plain ldap:// is refused at startup. Administrators are a group you name, and local admin accounts, checked before the directory is asked, mean there is always a way back in.

02

Your own domain

Every short link and every QR code encodes the origin you configure, never whatever arrived in the Host header. The management pages can live on a separate hostname from the short one.

03

Names you choose, or names that read off paper

Take a custom name, capitals and all, or let surl draw a random one: seven characters from an alphabet without 0, 1, l or o, so it survives being printed on a poster and typed back in.

04

One binary, a few variables, one file

A static Go executable with no runtime to install. Configuration comes from the environment, or an optional .env beside it, and never from a settings page. Every link lives in one SQLite file, which surl snapshots for you every night. If you'd rather run a container, there is a 15 MB image built FROM scratch: one executable, one certificate bundle, no shell. Nothing needs it.

05

Short links are forever

A name is never reissued, not even after its link is deleted. A withdrawn link answers 410 Gone with an explanation rather than vanishing into a 404. Redirects are always a bare 302, never an interstitial page.

06

Nothing to build, nothing tracked

One hand-written stylesheet and a handful of ES modules, no bundler, no node_modules. A visit adds one to a counter for that link and that day: no IP addresses, no user agents, no cookies on public pages, and no third party contacted.

What it does

Run it

As a binary

# one static executable, ./surl
make build
# a local administrator; the password is printed once
SURL_DATA_DIR=/var/lib/surl ./surl admin add ops-local
SURL_DATA_DIR=/var/lib/surl SURL_BASE_URL=https://s.example.org \
  ./surl serve

Or as a container, if you prefer

# the image, surl:latest
make image
podman run -d --name surl -p 127.0.0.1:8080:8080 \
  -v surl-data:/data -e SURL_BASE_URL=https://s.example.org \
  surl:latest
# a local administrator; the password is printed once
podman exec surl /surl admin add ops-local

Needs Go 1.26 to build (or an OCI runtime, if you prefer the image), a TLS-terminating reverse proxy in front, a directory to keep the database in, and optionally an Active Directory to sign in against. The README covers configuration, Apache, systemd, backups and restore.