Help us shape the future of Go! Take the 2026 Go Developer Survey

shortlink

module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 7, 2026 License: MIT

README

Go Reference

A self-hosted URL shortener in one binary: SQLite storage, custom slugs, expiry, QR codes, click analytics (per day, referrer, browser, device) and an embedded admin UI. No JavaScript framework, no external services, no cgo.

Who it's for

Clubs, small businesses, conference organisers and developers who want short links on their own domain, for posters (with a QR code), campaigns or docs, and who'd rather not hand click data to a third party. IP addresses are never stored.

Install

Prebuilt binaries for Linux, macOS and Windows (amd64 and arm64) are on the releases page. Each archive contains the shortlink binary (admin UI embedded), the README and the license; checksums.txt has the SHA-256 sums. For example, on Linux amd64:

curl -LO https://github.com/AbdeldjalilChougui/shortlink/releases/download/v0.1.0/shortlink_0.1.0_linux_amd64.tar.gz
tar xzf shortlink_0.1.0_linux_amd64.tar.gz

Or build from source with Go:

go install github.com/AbdeldjalilChougui/shortlink/cmd/shortlink@latest

Quick start

go build -o shortlink ./cmd/shortlink
SHORTLINK_TOKEN=change-me ./shortlink -addr :8080 -db shortlink.db -base-url https://s.example.com

Open http://localhost:8080/admin (Basic auth: any user, the token as password), or use the API:

curl -H "Authorization: Bearer change-me" -d '{"url":"https://example.com/2026/agenda","slug":"agenda","expires":"30d"}' \
     http://localhost:8080/api/links
# {"slug":"agenda","short_url":"https://s.example.com/agenda","url":"https://example.com/2026/agenda",...}

curl -I http://localhost:8080/agenda                                   # 302 → destination
curl -H "Authorization: Bearer change-me" 'http://localhost:8080/api/links/agenda?days=30'   # stats
method path
GET /{slug} 302 redirect (404 unknown, 410 expired)
POST /api/links {"url", "slug"?, "expires"?}. expires takes 72h, 30d, 2026-12-31 or RFC 3339
GET /api/links list
GET /api/links/{slug}?days=30 link + stats
DELETE /api/links/{slug} delete link and its clicks
GET /admin, /admin/links/{slug} UI
GET /admin/links/{slug}/qr.png?size=256 QR code PNG (64–1024 px)

How it works

  • Slugs. Random slugs are 6 characters from an alphabet without look-alikes (0/O, 1/l/I), generated with crypto/rand and retried on collision. Custom slugs are case-insensitive (COLLATE NOCASE) and can't shadow app routes (admin, api, …).
  • Redirects are 302 with Cache-Control: no-store. A 301 would be cached by browsers forever, so later clicks would go uncounted and expiry or deletion would be ignored.
  • Analytics. Each click stores the time, the UTC day, the referrer host and a coarse browser and device classification of the User-Agent (bots such as link-preview fetchers are labelled bot). The insert and the counter update share one transaction. Stats fill in empty days, so the per-day chart has no gaps.
  • Admin UI. Server-rendered templates embedded in the binary, with a CSS bar chart and the QR code inline. Because Basic-auth credentials are sent on cross-site requests too, state-changing admin requests go through Go's http.CrossOriginProtection (Fetch metadata and Origin checks) against CSRF.
  • Storage. modernc.org/sqlite (pure Go), WAL mode, foreign keys with cascade delete. QR codes come from github.com/skip2/go-qrcode.

Tests

go test -race ./...

They cover random and custom slugs (collisions, case-insensitive duplicates, reserved names), expiry (410), the redirect headers, click recording with referrer and UA, per-day stats with zero-filled days and a 7-day window, cascade delete, auth for UI and API, CSRF rejection of a cross-site form post, form validation errors, the QR PNG size, expiry parsing, and a User-Agent table.

Status and limitations

  • A single admin token. There are no user accounts or per-user link lists.
  • Links can't be edited after creation. Delete and recreate instead.
  • Days are bucketed in UTC.
  • The User-Agent classification is coarse. Bots that pretend to be browsers are counted as browsers.
  • There's no rate limiting on redirects or the API. Put it behind a proxy if it's exposed to abuse.

License

MIT. See LICENSE.

Directories

Path Synopsis
cmd
shortlink command
Command shortlink is a single-binary URL shortener with an admin UI.
Command shortlink is a single-binary URL shortener with an admin UI.
internal
store
Package store keeps links and click events in SQLite.
Package store keeps links and click events in SQLite.
ua
Package ua classifies User-Agent strings into a browser family and a device type.
Package ua classifies User-Agent strings into a browser family and a device type.
web
Package web serves redirects, the JSON API and the admin UI.
Package web serves redirects, the JSON API and the admin UI.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL