KU

Kutt

Modern open-source URL shortener with analytics

URL Shorteners ★ 11.1k stars Medium setup MIT

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.

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.

KuttShlinkYOURLS
StackNode, Postgres, RedisPHP, your choice of DBPHP, MySQL
Web UIBuilt in, modernSeparate web clientBuilt in, dated
AccountsMulti-userAPI keys with rolesSingle admin
Custom domainsPer userMultiple per instanceOne
Best forPeople clicking buttonsIntegrations and scriptsCheap 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