Auto-prefix URL — a small detail that saves minutes
cutty.dev recognizes when you paste an address without https:// and adds it automatically. It sounds trivial. In practice, it's the difference between two seconds and clicking 'try again'.
You type allegro.pl into the form. You click. And what?
In most URL shorteners, you get an error message at this point. Something like "this is not a valid URL". Because there is no https:// at the front. So you go back, add the protocol, click once more, and only now do you have your short link.
cutty.dev doesn't do this. It simply appends https:// itself and carries on, as if nothing happened.
I know how it sounds. Like a feature that barely deserves a paragraph, let alone an entire post. And yet, here I am writing about it, because this very trifle is a small manifesto for me of how I think about building the auto-prefix URL tool and all those other invisible conveniences.
Two seconds versus twenty
Let's count. Really, with numbers, because otherwise it's just talk.
First path: you paste, you have the link, you close the window. Two seconds. Maybe three.
Second path: you paste, error, read the message (a second or two to understand what's going on), add the protocol, click once more, only now the link. Fifteen, twenty seconds. Plus that tiny moment of irritation, that quiet "oh right, protocol," which costs nothing individually, but builds up.
And now multiply that by how many times a month you paste some address. For me, it's dozens. For someone who manages links professionally — hundreds. Suddenly, this "barely worth mentioning" trifle adds up to minutes per month. Per person.
This is exactly the arithmetic that most people overlook, because every single case looks too small to worry about.
What is actually sitting underneath
The mechanism is banal and I do not hide this at all. You paste the text into the field. Before anything is sent to the server, a piece of JavaScript checks whether what you entered starts with some protocol — http://, https://, ftp://, mailto:, anything. If it does not start with one, the system assumes that you mean a regular domain and prepends https:// to the front.
In practice:
allegro.plchanges tohttps://allegro.plgithub.com/twoj-profil/projekttohttps://github.com/twoj-profil/projektwww.gazeta.pltohttps://www.gazeta.pl
And if the protocol is already there, even if it's strange, the system does not overwrite it. It leaves it and continues to check what to do with it. http://example.com passes (allowed). ftp://example.com goes to the trash, because we only accept http and https. And javascript:alert(1) is out immediately, because that's not an address, it's an attempt to inject something nasty (classic XSS, thanks, but no).
Why do I have this, while half the market does not
It gets interesting here, because the answer is a bit funny.
The standard way to validate an address in a browser is the new URL(string) constructor. And this constructor is tough. Uncompromising. No protocol — it throws an error. new URL("allegro.pl") simply returns a TypeError and that's it.
And you know what? This is correct behavior. For the vast majority of applications, this is exactly what you want. But in a link shortening form, where a person copies an address from the browser bar or from someone's message, this strictness turns from an advantage into an obstacle. Suddenly, a tool that is supposed to save you time forces you to correct something that it perfectly understands anyway.
Bypassing this is literally a few lines:
function sanitizeUrl(input) {
const trimmed = input.trim();
const hasProtocol = /^[a-z][a-z0-9+.-]*:/i.test(trimmed);
const normalized = hasProtocol ? trimmed : "https://" + trimmed;
// dalej leci normalna walidacja przez new URL(normalized)
}
Five lines. A dozen seconds less friction, daily, for everyone who pastes addresses. Honestly, I don't know why this isn't the standard everywhere. I suspect it's because "that's just how the URL constructor works" and no one wanted to touch it. Inertia, not bad will.
A small digression on what "standardly" means
Because this story with new URL() is a great example of something broader. Many things in software that are considered "the way it's done" are actually just "how it turned out from the default settings of some library that no one questioned." The constructor's strict mode is not a decision by cutty designers or competitors — it is a decision by the authors of the URL specification, made in a completely different context, for completely different purposes.
And then the entire market inherits this default choice and calls it a "standard". I have the impression that the best products are born exactly in those places where someone paused and asked: wait, does this behavior actually serve my user, or just my library? Most often, the answer is: the library. And that is when it is worth using five lines of code to shift the focus back to the human.
There are more of these trifles than meets the eye
Auto-prefix is just one of many such elements that you won't find on any feature list. Because if they were listed there, each one individually would look ridiculously small:
- trimming spaces before validation, so that an accidental space at the end of a pasted address doesn't break everything
- preserving the anchor, meaning
#sectioninhttps://example.com/page#sectiondoes not disappear after shortening - query parameters (
?utm_source=test) pass through entirely, nothing is cut off along the way - domains with Polish characters,
źdźbło.pl, are automatically converted to punycode - Polish letters in custom slugs are replaced with ASCII (
różowy-linkbecomesrozowy-link) - dangerous protocols —
javascript:,data:,file:— are rejected with a specific, understandable message
(An automatic upgrade from http:// to https:// is still planned for when the target server supports encryption. This does not work live yet, so I won't pretend otherwise.)
Each of these details saves someone five, ten, thirty seconds and one sigh. Individually, nothing. Together, it is the difference between "works OK" and "I love using this."
What else I would like to add
Wishlist for the future, because thinking about it simply brings me joy:
- smart pasting — detect an address hidden inside a sentence and extract just the URL from between the words
- bulk pasting — a list of addresses, one per line, and immediately create a separate short link for each one
- suggested endings — based on the destination page title, propose three branded slugs to choose from
- Open Graph preview immediately after pasting, even before you click "shorten"
Each of these is once again a "five-line function that looks like nothing." And all together? That is exactly the boundary between a tool that is okay and one that you reach for every day without thinking. If you want to see how it works right now, paste anything without a protocol into cutty.dev — allegro.pl, github.com, news.ycombinator.com — and see that it doesn't trip you up at all.
Finally, a little bit personal
I long thought that products win with big features. That one impressive thing that can be shown on a slide and that people will write about. And yes, sometimes that is the case.
The longer I build cutty, the more strongly I feel that user loyalty is born elsewhere — in those hundreds of microseconds that no one consciously notices. You don't remember that the tool itself added https:// for you. You only remember the general impression that "it's somehow good to use" or "it's not annoying." This impression doesn't come from nowhere. It consists of dozens of five-line decisions that, individually, no one would defend to themselves as being "worth the time."
Maybe that is exactly what craftsmanship is about. Not in one single big move, but in the persistence to stand on the human's side time and again at a form — even if it only takes two measly seconds. And you? What has annoyed you lately in some tool so minor that it's almost embarrassing to admit? Because usually, that is exactly where the best work to be done lies.