Kutt

Self-hosted URL shortener with custom domains, per-link analytics, and zero build step required.

10.6Kstars
1.4Kforks
MIT License
JavaScript

Kutt is a modern, open-source URL shortener built for self-hosting. It lets you create branded short links on your own domain, track click statistics privately, manage users, and expose everything through a RESTful API — all without any frontend build pipeline.

Installation requires only Node.js 20+ and runs out of the box with SQLite as the default database. For more demanding deployments, Kutt supports PostgreSQL and MySQL/MariaDB, optional Redis caching and job queuing via Bull, and ships ready-made Docker Compose configurations for every combination.

Security and access control are first-class concerns: Kutt supports local email/password login, JWT cookie sessions, per-user API keys, and full OpenID Connect (OIDC) integration with any compliant identity provider. Rate limiting, bot detection, and Helmet-based HTTP hardening are all built in.

Customization is handled through a drop-in /custom folder — place CSS overrides, replacement images, or forked Handlebars templates there and they are served automatically, leaving the core codebase untouched and upgrade-safe.

What You Get

  • Custom domain support - Attach any domain you own to serve short links, with optional HTTPS enforcement and configurable CNAME/IP display in the settings UI.
  • Per-link click analytics - Track visits by browser, OS, country, and referrer for each shortened URL; stats are stored per-hour and optionally cached in Redis for fast retrieval.
  • Admin dashboard - Manage all users and links from a single interface, with filtering by ban status, anonymity, custom domain presence, and free-text search.
  • OpenID Connect login - Integrate with any OIDC-compliant identity provider (Auth0, Keycloak, Azure AD, etc.) to enable SSO; new users are provisioned automatically with a generated password.
  • RESTful API with API-key auth - Create, edit, delete, and query links programmatically; authentication supports JWT cookies, Bearer tokens, and per-user API keys via x-api-key header.
  • Link expiry and password protection - Set expiration timestamps and optional passwords on individual links; expired links are cleaned up automatically every 30 seconds via a built-in interval job.
  • Theme and template customization - Override CSS, images, and Handlebars views by placing files in the /custom directory without modifying core source, keeping upgrades clean.
  • Multi-database and Redis flexibility - Choose SQLite (default, zero config), PostgreSQL, or MySQL/MariaDB; optionally enable Redis for caching and Bull-powered asynchronous visit processing.

Common Use Cases

  • Branded marketing campaigns - A digital agency self-hosts Kutt on a client’s domain to shorten campaign URLs, track clicks by country and referrer, and avoid sharing data with commercial shorteners.
  • Corporate internal link service - An IT team deploys Kutt behind their IdP using OIDC so employees can create memorable short links for internal wikis and dashboards using existing SSO credentials.
  • Privacy-conscious content distribution - A newsletter operator self-hosts Kutt to shorten affiliate or tracking links without routing user click data through third-party analytics services.
  • Developer automation pipelines - A DevOps team calls Kutt’s REST API from CI/CD scripts to generate short links for deployment previews, linking build artifacts to short, shareable URLs.
  • Multi-tenant link management platform - A hosting provider runs a single Kutt instance, assigns each customer a custom domain, and lets them manage their own links through the UI or API.
  • Community and event coordination - A conference organizer uses Kutt to create human-readable short links for session schedules, speaker bios, and resource downloads that redirect to long CMS URLs.

Under The Hood

Architecture Kutt follows a clean layered architecture built on Express, with distinct layers for routing, request handling, database queries, and server-side rendering. The entry point initializes middleware, registers Passport strategies, and mounts separate routers for server-rendered pages and the versioned JSON API — keeping HTML and API concerns fully isolated. Business logic lives in handler modules (links, users, domains, auth) that delegate all database access to a parallel set of query modules, preventing SQL from leaking into handlers. The redirect hot path is optimized with Redis key lookups before hitting the database, and visit recording is offloaded to a Bull queue (or a synchronous in-process fallback when Redis is absent), keeping redirect latency minimal under load. A CustomError class and a centralized error middleware ensure consistent error shapes across all endpoints.

Tech Stack The backend is Node.js 22 with Express 4 and Handlebars (hbs) for server-side HTML rendering. Database access uses Knex 3 for SQL abstraction supporting better-sqlite3 (default), PostgreSQL via pg, and MySQL/MariaDB via mysql2 — switchable entirely through environment variables. Redis integration is optional and powered by ioredis with Bull for background job queuing. Authentication is handled by Passport.js with three strategies: JWT (cookie-based), local (email/password), and API-key via x-api-key header; OIDC is supported through openid-client with dynamic issuer discovery. Visitor analytics enrich data using useragent parsing and geoip-lite for country lookups. Deployment targets Docker (Alpine-based image, multi-variant Compose files) or bare Node.js with npm ci for reproducible installs.

Code Quality The codebase has no test files — there is no test framework, no test directory, and no test scripts in package.json. Error handling is partially structured: a CustomError class with status codes exists and is used in some paths, but many async handlers rely on a generic asyncHandler wrapper without typed error discrimination at the call site, leading to opaque failure modes in edge cases. Code style is consistent within modules but lacks enforced linting or formatting tooling — the README notes that double quotes were standardized in v3.2.0 manually rather than through a linter. Type safety relies solely on JSDoc-style @types devDependencies for editor support; there is no TypeScript compilation or runtime schema validation. Documentation is strong for operators (comprehensive README, environment variable table, Docker examples, customization guide) but sparse in the source code itself.

What Makes It Unique Kutt’s defining differentiator for self-hosters is genuine zero-configuration readiness: the default SQLite database requires no external service, the initial admin account is created interactively on first run, and the only mandatory environment variable in production is JWT_SECRET. The customization model — a /custom folder that overlays CSS, images, and Handlebars views without forking core templates — means operators can apply full white-label themes while staying on upstream releases. Visit analytics use hourly-bucketed, denormalized rows with JSON columns for countries and referrers, which keeps query complexity low and makes stats fast without a time-series database. The optional Redis layer provides a transparent upgrade path: the same code path works with or without Redis, using an in-process fallback that runs the Bull processor synchronously, so operators can scale up caching and async processing without changing application logic.

Self-Hosting

Kutt is released under the MIT License, which is one of the most permissive open-source licenses available. You are free to use it commercially, modify the source, redistribute it, and incorporate it into proprietary systems without any obligation to open-source your changes or pay license fees. There are no copyleft restrictions, no contributor license agreements, and no distinction between community and enterprise editions — the full feature set is available to anyone who runs the code.

Running Kutt yourself is straightforward compared to many self-hosted tools. The only hard dependency is Node.js 20+; everything else — database, Redis, mail — is optional and defaults to SQLite out of the box. Docker Compose files are provided for four configurations (SQLite, SQLite+Redis, Postgres+Redis, MariaDB+Redis), making initial deployment a single command. That said, production operations are your responsibility: you manage SSL certificates for custom domains manually, configure your own backups for the SQLite file or Postgres database, handle scaling if your link volume grows, and apply security updates by pulling new releases. The built-in cron job that purges expired links runs in-process, so you also need to ensure the Node process stays running — typically handled via a process manager like pm2 or the Docker restart policy.

Compared to the hosted service at kutt.to, self-hosting gives you complete data ownership and the ability to attach unlimited custom domains, but you trade away managed uptime, automatic upgrades, and centralized support. The hosted service handles SSL automation, database backups, and infrastructure scaling on your behalf. There is no paid enterprise tier or SLA offering from the Kutt project — commercial support would need to come from your own team or a third-party contractor. If your organization requires high availability, you would need to implement your own clustering strategy (the codebase is cluster-aware via NODE_APP_INSTANCE) and set up Redis for shared session state across instances.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack

No spam. Unsubscribe anytime.

Join 750+ subscribers
No spam. Unsubscribe anytime.

Search