Dev.to AI 🤖 Ai 👁 0 📖 2 min read

My short links stopped logging clicks after one visit each — 301s cache themselves out of my logs

A few months back I built myself a small short-link service on freeq.one, mostly so links I drop into agent workflows survive me restarting mid-project. The first version answered every redirect with HTTP 301. That decis

A few months back I built myself a small short-link service on freeq.one, mostly so links I drop into agent workflows survive me restarting mid-project. The first version answered every redirect with HTTP 301. That decision was the bug.

After a couple of weeks I noticed one of my heavily-used links had a click graph shaped like a cliff: healthy for days, then flat forever after. The destination, meanwhile, was clearly still getting steady traffic. People and agents were using the link — my server just never heard about it anymore.

The problem is that 301 means permanently moved. That's not a label, it's permission: clients are entitled to cache the redirect and never hit my domain again. So every browser and HTTP library that followed the link once kept a private copy of the mapping. Repeat clicks went destination-ward directly — no request to me, no count, no log entry. It got worse when I later repointed that link to a new page: cached clients kept sailing to the old destination, so my update only existed for first-time visitors.

The fix was to stop special-casing: every live link now answers 302. A temporary redirect makes no promise, so clients re-ask me on every click, and that hop doubles as my measurement point. The cost is one extra round trip per click, which rounds to nothing at this scale. I reserve 301 semantics for URLs I'm retiring outright — old domains, dead paths — where "never come back" is exactly the behavior I want.

The general rule I extracted: pick a redirect status code by asking whether you still claim to operate that URL as an entry point. If yes, the redirect must be temporary, because a permanent code hands state to clients that you cannot invalidate later. Status codes aren't performance hints; they're contracts about who is allowed to cache the truth.

That 302-first behavior is now baked into the short links I hand out daily (create + owner-only click stats: https://x402.freeq.one/tools/shortlink_create.html) — and I learned it the expensive way, by shipping the wrong status code everywhere first.

📰 Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.