I run a free link shortener as a side project. A few thousand people use it a day, and about 75,000 links sit in the database. Once a week I go through them looking for the ones that are up to no good.
Last week’s sweep turned up one destination that had taken 89,826 clicks in 48 hours. More than everything else on the site combined.
I deleted the links from the database. Two minutes later, they were back.
What followed was three rounds of me deleting and the operator re-registering. Running a link shortener meant I got to watch the whole operation from underneath: how the links hid what they were doing, what was sending me the traffic, and where it was going.
One vocabulary note before we start: the part of a short link after the slash is the slug, and you get to pick your own. Mine look like zip1.io/jip. The slug is jip. Remember the slug 🐌.
How the abuse sweep works
Most bad links are caught when you first shorten one. Submitted URLs go through a blocklist, then Google Safe Browsing.
Some still get through. That’s what the weekly scan is for.
The scan is read-only. It scores all ~75k links for cheap domains, brand names in odd places, nested shorteners, and paths that look like login or phishing pages. Claude Code runs the sweep, proposes blocks, and I decide what actually gets deleted from production. Each scan usually expands the create-time blocklist.
I don’t do this only to be a good online citizen. If I didn’t, Google would eventually mark the domain as a bad actor and stop sending search traffic.
The bad link arrives
The destination was hai8g.com/4/11395320 — an ordinary .com with a numeric path. Nothing about the URL looked suspicious.
What gave it away was volume: three slugs, all pointing at the same destination, drawing thousands of clicks.
What the link actually did
I fetched the destination from my laptop and got 963 bytes of HTML whose only job was: send this visitor to google.com. Harmless.
So I fetched it twice more — once from a rented datacenter server (where an automated scanner would come from), and once pretending to be an Android phone opening the link inside the Facebook app (what the click data said real visitors were).
| What I fetched it with | What came back |
|---|---|
| Ordinary desktop browser | 963 bytes → redirect to google.com |
| Server in a datacenter | Redirect to yahoo.com |
| Android phone, from Facebook | 42,748 bytes of machinery |
One address. Three answers. Depending on who asked.
The 42k-byte payload was scrambled on purpose. Untangled, the page:
- looked for fingerprints that Selenium / Puppeteer leave behind
- checked for an ad blocker
- sampled cursor/touch movement (~80 positions) to confirm a real hand
- fingerprinted GPU, screen, battery, timezone
- and if any check failed: played a success animation, then silently did nothing
That last detail is my favourite. It doesn’t show an error. It congratulates you and bins you, so you never learn you were caught.
The domain itself isn’t disposable either. Ahrefs gives hai8g.com a Domain Rating of 50 (this blog is 43; zip1.io is 24). That strength comes from hundreds of thousands of inbound links across pirate streaming and free-music sites — the same ad tag repeated everywhere. Infrastructure, not a throwaway. Malwarebytes flags it as riskware. Nothing flags zip1.io, which is exactly why it was worth using as a sideline.
How the links reached people
Four in five clicks came from a Facebook-owned client. The in-app browser is embedded in the Facebook app, so someone tapped a link without leaving it. FacebookBot is Facebook’s crawler — it fetches a link every time somebody posts it. Thousands of crawler hits means the link was posted a great many times, across a great many accounts.
Who clicked, and who sent them
The people clicking were overwhelmingly in Latin America. Mexico, Colombia, and Venezuela alone were ~40% of clicks; seven of the top nine countries are Spanish-speaking. Whatever the page said, it said it in Spanish.
Whoever created the short links was somewhere else entirely: Asia-Pacific, then a Pakistani address. They almost certainly dont speak Spanish.
That gap isn’t a coincidence. Buried in the page were fields for logo, headline, button label, and terms text — filled in per country by the ad network. Localisation is the network’s job. The buyer only has to deliver people.
Three rounds of fix and purge
I assumed the important thing was the destination, so I deleted the slugs and blacklisted the domain.
Four hours later they were back — same three slugs, pointing at two new domains that only forwarded to hai8g.com. They’d added a hop in the middle.
Because the product lets you choose a custom slug, they recreated zip1.io/jip. Whatever they were spreading on Facebook had that slug baked in. It had to keep working.
The fix was to reserve burned slugs instead of releasing them. When I remove an abusive link now, its slug stays claimed. Anyone trying to reuse it gets the ordinary alias already exists message deliberately identical to a normal collision.
zip1.io/jip returns not found, and it will keep doing that. The Facebook posts are still out there, but they lead nowhere. Even a day later they were still sending traffic my way.
What I’d tell anyone running open infra
- Create-time checks aren’t enough. Blocklist + Safe Browsing catch a lot; volume and nested hops still sneak through.
Deleting isnt enough if slugs are reusable. Reserve them, or the campaign’s printed URLs keep working.
Dont trust a validate URL” helper as a create-gate. On my stack, validation can still say a URL looks fine while create refuses it for abuse reasons.
Cloaking is the default for serious campaigns. Desktop, datacenter, and in-app Facebook can see three different sites.
The project
The site is zip1.io a free, no-signup URL shortener I forked, rebranded, and shipped (mostly with Claude Code). Claude also does a lot of the security grunt work. I make the judgement calls on takedowns.
It makes no money. It costs me about $27/month to run.
If you run something similarly open — no auth, public create, custom slugs — I’d love to hear how you handle the third round of “they’re back.
Originally published on my blog. Lightly adapted for Dev.to.
Top comments (0)