Shlink
The definitive self-hosted URL shortener
Shlink is a self-hosted URL shortener with a powerful REST API, detailed visit analytics, QR codes, and a separate web client. It supports custom domains and tags for organizing links.
Key features
- Powerful REST API
- Detailed visit analytics
- QR code generation
- Multi-domain support
Pros & cons
Strengths
- Excellent API
- Geolocation analytics
Trade-offs
- Web UI is a separate component
Shlink replaces
Last reviewed Sep 13, 2026 · 850 words
Shlink is two containers, and the second one is optional. The server is a PHP application that exposes a REST API and redirects short links; the web client is a separate static app that talks to that API from your browser. People who skip the web client and never notice are the ones Shlink was built for: developers who create links from scripts, CI pipelines or a n8n flow and want per-link visit stats with country, browser and referrer. If you want a shortener you administer by clicking, YOURLS is older and simpler and I say so below. If you want the best API in the category and analytics that stand up, Shlink is the pick, at 5,261 GitHub stars under MIT.
The server container and its three decisions
services:
shlink:
image: shlinkio/shlink:stable
environment:
DEFAULT_DOMAIN: s.example.com
IS_HTTPS_ENABLED: "true"
DB_DRIVER: postgres
DB_HOST: db
DB_NAME: shlink
DB_USER: shlink
DB_PASSWORD: change-me
GEOLITE_LICENSE_KEY: your-maxmind-key
ports:
- "127.0.0.1:8080:8080"
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_DB: shlink
POSTGRES_USER: shlink
POSTGRES_PASSWORD: change-me
volumes:
- ./pgdata:/var/lib/postgresql/data
Decision one is the domain. DEFAULT_DOMAIN is what short links are built on, so it should be the shortest domain you own; IS_HTTPS_ENABLED must be true behind a TLS-terminating proxy or generated links come out as http://. Decision two is the database. SQLite works and is what Shlink defaults to, and it is fine for a personal shortener; the catalogue tags Shlink as Postgres for a reason, because every visit is a row, a link that gets shared once can write 10,000 rows in an afternoon, and Postgres handles that without locking the redirect path. The Postgres for everything argument applies. Decision three is geolocation, covered next. Shlink is Medium difficulty because of these choices, not because any one of them is hard; it needs about 256 MB of RAM.
Analytics need a free MaxMind key
Country and city on visits come from the GeoLite2 database, which MaxMind gives away but only with an account and a licence key. Without GEOLITE_LICENSE_KEY every visit is recorded but shows no location. Sign up at maxmind.com, generate a key, set the variable, and Shlink downloads and refreshes the database itself. Referrer, user agent and timestamp are tracked regardless. Note that this makes Shlink a visitor-tracking system for whoever clicks your links, and it does not anonymise by default; ANONYMIZE_REMOTE_ADDR is true out of the box so IPs are truncated before storage, and I would leave it that way and mention it in a privacy note if the links are public.
Get an API key, then decide whether you need the UI
docker compose exec shlink shlink api-key:generate
That key is your credential for everything: the REST API, the web client, the CLI. Creating a link is one request:
curl -X POST https://s.example.com/rest/v3/short-urls \
-H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
-d '{"longUrl": "https://example.com/some/long/path", "customSlug": "launch"}'
Tags, expiry dates, max visits, custom slugs, per-device redirect rules and QR codes are all parameters or endpoints on the same API. The web client (shlinkio/shlink-web-client) is a separate container serving a static single-page app; you point it at your server URL and key from the browser, or bake a servers.json into it. Run it on a different hostname from the short domain, since the short domain should serve nothing but redirects. Or do not run it at all and use the CLI inside the container, which covers every operation.
Shlink against YOURLS and Kutt
| Shlink | YOURLS | Kutt | |
|---|---|---|---|
| Language | PHP | PHP | TypeScript |
| Database | Postgres, MySQL, MariaDB, SQLite | MySQL | Postgres and Redis |
| API | Full REST, versioned | Simple, key-based | REST |
| Admin UI | Separate container | Built in | Built in |
| Visit analytics | Country, city, browser, referrer | Click counts, basic | Basic |
| Multiple domains | Yes | No | Yes |
| Users | Single tenant, many API keys | Single admin | Multi-user with sign-up |
YOURLS has been around longer and is the right answer for one person who wants a UI, a MySQL database they already have, and no interest in scripting. Kutt is the one with user accounts, if you are running a shortener for a team that signs in. Shlink wins on API quality, multi-domain and analytics depth, and it is the only one of the three I would put a public campaign behind. The URL shorteners category lists the rest of the field.
What I'd do
Shlink server on Postgres, on the shortest domain you own, HTTPS on, MaxMind key set, one API key per client that creates links. Skip the web client until you find yourself wanting to browse stats, then run it on an admin hostname behind your SSO. Back up the Postgres volume and the environment file; the container itself holds nothing. Leaving Bitly for this costs one afternoon and removes the per-link pricing and the tracking you did not ask for.
Compare Shlink
11 head-to-head comparisons.
Similar url shorteners apps
Dub
URL ShortenersOpen-source link management platform
Replaces Bitly, Rebrandly
YOURLS
URL ShortenersYour own URL shortener with stats and plugins
Replaces Bitly
Kutt
URL ShortenersModern open-source URL shortener with analytics
Replaces Bitly, TinyURL
Sink
URL ShortenersServerless URL shortener with built-in analytics
Replaces Bitly
Polr
URL ShortenersQuick, modern and open-source link shortener
Replaces Bitly
Slash
URL ShortenersOpen-source, self-hosted bookmarks and link shortener
Replaces Bitly, go links