Self-hosted service
SinTan1729/chhoto-url avatar
SinTan1729/chhoto-url

Chhoto URL: a Rust link shortener that stops at link shortening

A simple, blazingly fast, selfhosted URL shortener with no unnecessary features; written in Rust.

970 stars95 forksRustMIT

At a glance

What is it?
Chhoto URL is a self-hosted shortener built on Actix Web and SQLite, packaged as a container under 4.5 MB compressed. Its value is what it refuses to do, and that refusal is also its main constraint.
Who is it for?
Adopt Chhoto URL if you want one small container, a password, and an SQLite file you can copy, and if per-link hit counts without visitor data are enough. Do not adopt it if you need multiple users, per-user quotas, or an admin interface that survives a compromised password, because the project states that user management will not be implemented.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem is not shortening, it is everything around shortening

Anyone can shorten a URL with a few lines of code. The hard part is the surrounding machinery that most shorteners accumulate: user accounts, analytics dashboards, consent banners, and a Java or Node runtime that turns a 20 MB service into a 200 MB image. The README is explicit about the origin story. The author liked the idea behind simply-shorten but disliked that a simple app carried a roughly 200 MB Docker image, mostly the bundled Java runtime, so the rewrite happened in Rust with hit counting added. The target user is therefore narrow and identifiable: someone who wants a short domain, a password, and a database file, and who is willing to give up multi-user administration to get a container measured in single-digit megabytes. The README states the scratch image is under 4.5 MB compressed and the alpine one under 8.5 MB compressed, with RAM use under 10 MB in regular operation. Those figures come from the project's own documentation, not from independent measurement.

Actix Web, SQLite, and a list of features that were deliberately left out

The architecture is a Rust backend on Actix Web with a frontend in plain HTML and vanilla JavaScript styled with Pure CSS. Links live in an SQLite database, which the README describes as configured to be ACID by default, with options exposed for tuning the database. That is the whole stack. Redirects are direct: opening a short link sends the browser to the target, with no interstitial page, which the README calls out as a deliberate difference from alternatives that route through a redirection screen. Hit counting records that a hit occurred and nothing else, which is a privacy decision with a functional cost: you get a number per link, not referrers, geography, or unique visitors. The README also maintains a section titled Bloat that will not be implemented, listing tracking, user management, cookies and popups, and paywalls. That section is the most useful part of the documentation for an evaluation, because it converts an open-ended feature roadmap into a fixed boundary. If your requirement falls on that list, the answer is no, and it will stay no.

Getting it running: container, compose file, password and API key

The README points installation and configuration to docs/INSTALLATION.md and CLI usage to docs/CLI.md, and states that the project ships as a Docker container with a provided compose file. The repository also carries podman and quadlet topics, so the container path is the intended one. What the top-level README does confirm about configuration is the authentication model: basic authentication using a provided password, with support for supplying a hashed password and a hashed API key rather than plaintext. The README warns that this password is not encrypted in transport and recommends a reverse proxy such as caddy for SSL. That is the single most important operational detail in the document, and it means the deployment is two pieces, not one: the container plus a TLS terminator in front of it. Beyond that, the concrete environment variable names and volume paths live in docs/INSTALLATION.md, which is not included in the material available here, so I cannot list them without guessing. Treat that file as required reading before you write a compose file.

Public mode is the interesting setting and the riskiest one

The README describes a public mode where anyone can add links without authentication, while deleting or listing links still requires the admin password. It also notes that the frontend can be disabled entirely, and that a forced expiry time can be set for public instances. That combination is a coherent design: an open submission endpoint with a bounded lifetime and a private management surface. The failure mode follows directly from it. An unauthenticated write endpoint on the public internet will be found and used, and the only stated mitigations are expiry and the ability to turn the frontend off. There is no mention of rate limiting, CAPTCHA, or a blocklist in the material available, so if you enable public mode you should assume you need to handle abuse at the proxy layer or accept that links expire quickly. The README frames forced expiry as useful for public instances, which reads as the author's own acknowledgment of this trade-off.

Where it is the wrong tool

The README answers this more directly than most projects do. Under user management it states that if you need a shortener for a whole organization, you should either run separate containers for everyone or use something else. That is a real constraint, not a modesty flourish: there is no shared link inventory, no per-user ownership, no quota accounting, and no audit trail beyond simple debug logs. A team that wants one short domain with departmental separation will end up running several containers and several databases, which multiplies the upgrade surface rather than reducing it. The authentication model has a second limit. A single password plus an API key means there is no way to revoke one client's access without rotating the credential for every client. If you embed the API key in a script or a browser extension, that script now holds the same authority as your admin session. The README's own recommendation of a reverse proxy for TLS is a sign that the security boundary is expected to sit outside the application.

Compared with a general-purpose link management platform

The obvious alternative class is the full link management platform, of which the best known self-hostable example is Linkding or a similar bookmark-plus-shortener tool, and the hosted commercial option is Bitly. The difference in approach is not a feature checklist, it is where state lives and who it belongs to. Chhoto URL stores links in a local SQLite file with no outbound dependency; you own the file and the domain, and nothing leaves the host. A commercial shortener stores links on someone else's infrastructure, gives you click analytics with referrers and geography, and charges by volume. Between those two poles, the decision is about whether you need the analytics or the ownership. If you need per-campaign attribution, UTM handling, or team roles, Chhoto URL is the wrong side of that line and the README says so. If you need a short domain that resolves quickly and a database you can back up with cp, it is the right side.

Maintenance, releases and the MIT licence

The release cadence visible in the repository is tight: 7.5.1, 7.5.2 and 7.5.3 all landed within a four-day window in early September 2026, and the last push to the default branch is dated 2026-09-07. Patch-level releases at that frequency usually mean bug fixes and dependency bumps rather than feature work, which matches the README's statement that the project is considered complete rather than dead, with bug fixes and security updates promised but new features unlikely. For an operator, that is a low but nonzero maintenance load: you should expect to rebuild the container when a security advisory lands, and the small image size keeps that cheap. The licence is MIT, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is a permissive licence and it is compatible with shipping the container inside a commercial product, but I am not a lawyer and this is not legal advice; if you redistribute it, read the SPDX identifier and the LICENSE file in the repository rather than this paragraph.

The judgement

Chhoto URL is a small, finished tool with a clear opinion about what a shortener should be. The opinion is defensible: direct redirects, no tracking, no accounts, one SQLite file, a container you can pull in seconds. The cost is that anything requiring shared administration, per-user access, or visitor analytics is out of scope permanently, by the author's stated intent rather than by omission. The demo instance at demo.chhoto.link, with its database cleared every 15 minutes, is the fastest way to see the UI and the redirect behaviour before committing to a deployment. What you cannot verify from the README alone is how the service behaves under concurrent writes to SQLite or what happens when the data volume is misconfigured, so test those two things on your own host before pointing a production domain at it.

Editorial conclusion

Adopt Chhoto URL if you want one small container, a password, and an SQLite file you can copy, and if per-link hit counts without visitor data are enough. Do not adopt it if you need multiple users, per-user quotas, or an admin interface that survives a compromised password, because the project states that user management will not be implemented. Before deploying, verify three things on your own host: that your reverse proxy terminates TLS, since the README says the password is not encrypted in transport; that the data directory you mount actually persists across container recreation; and how the public mode and forced expiry settings behave with your intended traffic.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. SinTan1729/chhoto-url on GitHub
Community notes

Community notes