Kutt
Modern open-source URL shortener with analytics
Kutt is a free and open-source URL shortener with a clean interface, custom domains, link expiration, and detailed statistics. It offers both a web UI and a REST API.
Key features
- Custom domains and slugs
- Link expiration
- Visit statistics
- Public REST API
Pros & cons
Strengths
- Clean modern UI
- Free and ad-free
Trade-offs
- Requires Postgres and Redis
Kutt replaces
Last reviewed Aug 26, 2026 · 808 words
Kutt costs you 3 containers and about 512 MB of RAM to shorten links, which is the trade in 1 sentence: you get a modern web UI, per-link statistics, expiry, password protection, and custom domains, and you pay for it with Postgres and Redis sitting next to a Node app. That is the right deal if humans will use the interface. If only scripts will call it, Shlink is the leaner shape, and if you want 1 folder of PHP on shared hosting, YOURLS still exists.
The stack: Node, Postgres, Redis, and 1 short domain
services:
kutt:
image: kutt/kutt:latest
depends_on: [postgres, redis]
ports:
- "3000:3000"
environment:
DEFAULT_DOMAIN: go.example.com
JWT_SECRET: a-long-random-string
DB_HOST: postgres
DB_NAME: kutt
DB_USER: kutt
DB_PASSWORD: change-me
REDIS_HOST: redis
ADMIN_EMAILS: [email protected]
MAIL_HOST: smtp.example.com
MAIL_PORT: 587
MAIL_USER: [email protected]
MAIL_PASSWORD: change-me
MAIL_FROM: [email protected]
restart: unless-stopped
postgres:
image: postgres:16
environment:
POSTGRES_DB: kutt
POSTGRES_USER: kutt
POSTGRES_PASSWORD: change-me
volumes:
- ./pg:/var/lib/postgresql/data
redis:
image: redis:alpine
The mail settings are not optional decoration. Account creation is verified by email, so without a working SMTP relay you cannot log in to your own shortener. DEFAULT_DOMAIN is what every short link is built on, so pick the shortest domain you own and treat it as permanent; links you have already handed out embed it. Put Caddy in front on port 443 and forward to 3000.
Close the doors before you open the port
DISALLOW_REGISTRATION: "true"
DISALLOW_ANONYMOUS_LINKS: "true"
Register your own account first, then set both of these and restart. An open shortener on a clean domain becomes phishing infrastructure within days, not weeks: spam crews scan for them, and the payoff for you is your domain on the Safe Browsing blocklist and your mail from that domain landing in junk. ADMIN_EMAILS grants the admin view, where you can see and delete every link on the instance, which you will want the first time someone reports one.
What each link gets
A custom slug or a random 6-character one, an optional expiry, an optional password, a description, and a stats page showing visits over the last day, week, and month, split by browser, operating system, referrer, and country. Stats are aggregate counts, not per-visitor logs, which is the right level for a personal or small-business instance. Redirects are served by the same Node process, so every link you have ever handed out depends on this instance staying up; that is the real operational commitment of any shortener. Keep it on a host you do not rebuild casually, and put the Postgres volume in the nightly backup, because the link table is the product and Redis holds nothing you would miss. Custom domains are per user: add links.yourbrand.com in settings, point a CNAME at the Kutt host, and make sure your reverse proxy answers for that hostname too, or the link will resolve to a certificate error.
The API and the extension do the daily work
curl -X POST https://go.example.com/api/v2/links \
-H "X-API-KEY: $KUTT_KEY" \
-H "Content-Type: application/json" \
-d '{"target": "https://example.com/very/long/path", "customurl": "launch", "expire_in": "7 days"}'
The API key comes from your settings page, and the same key drives the official Chrome and Firefox extension, which turns the current tab into a short link in 1 click. Most of my links are created from the extension or from a script; the web UI is where I go to look at numbers.
Kutt against Shlink and YOURLS
| Kutt | Shlink | YOURLS | |
|---|---|---|---|
| Stack | Node, Postgres, Redis | PHP, your choice of DB | PHP, MySQL |
| Web UI | Built in, modern | Separate web client | Built in, dated |
| Accounts | Multi-user | API keys with roles | Single admin |
| Custom domains | Per user | Multiple per instance | One |
| Best for | People clicking buttons | Integrations and scripts | Cheap PHP hosting |
Shlink is the one I reach for when the shortener is a backend for other software: it is API-first, tracks visits in more detail, and its web UI is a separate static app you can skip. YOURLS is the oldest of the 3 and shows it, but it runs anywhere PHP runs and has a plugin ecosystem. The full field is on the URL shorteners page.
What I'd do
Kutt, behind Caddy, with registration and anonymous links disabled on day 1, an SMTP relay configured before the first login attempt, and a 2-letter domain if I can get one. Postgres and Redis in the same compose file, nightly pg_dump of the database, Redis treated as disposable. If I later find the UI is something I open twice a year, I would migrate to Shlink and drop the memory footprint by half.
Compare Kutt
13 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
Sink
URL ShortenersServerless URL shortener with built-in analytics
Replaces Bitly
Shlink
URL ShortenersThe definitive self-hosted URL shortener
Replaces Bitly, Rebrandly
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