Short URLs are one of the oldest tricks on the web, yet most teams use them without understanding what happens between a click and a redirect. That gap costs real money: the wrong status code kills analytics, the wrong domain kills click-through, and the wrong infrastructure makes every link feel slow.
This guide explains how modern link infrastructure works — code generation, redirect semantics, bot filtering, security, and performance — and shows you the exact decisions that separate a toy shortener from a production link platform. Every concept maps to a real endpoint in the yas.sh API, so you can verify each claim yourself.
Anatomy of a short link
A short link is a lookup table entry exposed as a URL. The destination (originalUrl) is stored in a database keyed by a short code, and the shortener's edge serves an HTTP redirect for that code.
https://yas.sh/Ab3xK9q
└────┬───┘ └──┬──┘
domain code (7 chars, base62)
When a browser requests /Ab3xK9q, the platform:
- Parses the code from the path.
- Looks it up (database index on
shortCode, ideally cached in Redis/Valkey at the edge). - Checks lifecycle: is the link expired? Has it hit its click limit? Is it password-protected?
- Records the click (bot-filtered, IP-hashed) asynchronously — never blocking the redirect.
- Responds
302 Location: <destination>in ~30–50ms.
The critical design point is step 4: the click event is written asynchronously so a slow analytics write can never delay a redirect. The redirect implementation follows exactly this pattern — the browser gets its redirect immediately, and analytics catch up in milliseconds.
How short codes are generated
There are two families of codes: random and custom.
Random codes must be short (7 characters is the norm), collision-resistant, and unguessable enough that one user's links can't be enumerated by another. The standard technique is base62 encoding: an alphabet of 0-9a-zA-Z gives 62 possibilities per character, so 7 characters yield 62⁷ ≈ 3.5 trillion combinations. Codes are generated with a CSPRNG, and the database's unique index catches the astronomically rare collision — the generator simply retries.
Custom aliases (branded slugs) are user-chosen, which means they need governance:
- Character whitelist:
^[a-z0-9-_]{3,30}$— no spaces, no unicode surprises, no URLs that look like punctuation soup. - Reserved-word protection: aliases like
api,admin,login,docs, or_nextare rejected so they can never shadow real routes. - Uniqueness across both
shortCodeandcustomAlias— a user alias must not collide with a random code or another alias.
curl -X POST https://yas.sh/api/v1/links \
-H "Authorization: Bearer yas_live_..." \
-H "Content-Type: application/json" \
-d '{"originalUrl":"https://example.com/very/long/path?utm_source=newsletter","customAlias":"launch"}'
# → 201 { "shortCode": "launch", "url": "https://yas.sh/launch" }
Aliases are case-normalized to lowercase so Launch and launch can't be registered as different links pointing at different places — a classic phishing footgun on other platforms.
301 vs 302: the decision that matters
The status code you redirect with determines whether you can measure anything at all.
| 301 Permanent | 302 Temporary | |
|---|---|---|
| Browser caching | Cached aggressively; repeat visits skip the shortener | Not cached; every click passes through |
| Search engines | Link equity passes to destination | Equity passes with attribution notes |
| Click analytics | Lost after first visit | Complete |
| Typical use | Permanent domain moves | Short links, campaigns, A/B tests |
Short link platforms use 302 because the entire business model is measuring the click. With a 301, the second click never reaches the platform, so counts under-report badly. If you need a permanent redirect for SEO purposes, put the 301 on your destination server and keep the short link layer on 302 — the 301 vs 302 deep-dive covers this in detail.
Security: the parts nobody sees
A production shortener defends against four specific attacks:
Open redirect abuse. The only safe destination protocols are http: and https:. Schemes like javascript:, data:, file:, or protocol-relative tricks are rejected at validation time — both in the API schema and again at redirect time. yas.sh validates with Zod at the API boundary, which is the pattern to copy in any stack.
Phishing via reserved names. Without reserved-word protection, someone registers the alias yassh-login and typosquats your own brand. The platform maintains a blocklist of its own routes and common phishing suffixes, and rejects lookalike aliases on creation.
Password links leaking. Password-protected links must never put the password in the URL — query strings end up in browser history, referrer headers, and server logs. yas.sh serves a password gate page that submits via POST, then redirects on success. See password-protected links in practice.
Brute force. Password gate attempts are rate-limited per link per IP (10/min), and session cookies are HttpOnly + SameSite=Lax with hashed tokens server-side.
Attacker: POST /sale-deck password=admin → 401
POST /sale-deck password=123456 → 401
POST /sale-deck password=Summer2026! → 302 (blocked at attempt 11)
Bot filtering and honest analytics
Raw click counts include crawlers, link previewers (Slack, iMessage, Facebook), and security scanners — sometimes 30–40% of all hits. Analytics only become trustworthy when bots are filtered at ingest:
- User-agent heuristics:
bot,crawl,spider,slurp,mediapartners,headless,puppet, etc. - Device/browser detection via a real parser (Bowser), not regex guessing.
- IPs stored hashed, never raw — you get uniqueness signals without storing personal data.
The result is a click count you can present to a board without caveats. The dashboard's totals and the API's /analytics/overview endpoint both return bot-filtered numbers, with the raw event stream still available for audit if you need it.
Performance budget
Redirect latency is the product's speed of light. The budget that matters:
| Metric | Budget | Why |
|---|---|---|
| Redirect p95 | < 60ms | Email and QR links are clicked on mobile networks; every 100ms costs clicks |
| Code lookup p95 | < 5ms | Needs an index on shortCode; cache at the edge for hot links |
| Click ingest | async, ≤ 2s | Never on the redirect path |
| QR render | < 150ms | Generated on demand at 128–1024px, cached 1h |
The system status page shows live numbers against these budgets, and GET /api/health returns latency per request for your own monitoring.
Measuring success: the workflow
- Create — short link with alias, optional password/expiry (see all options).
- Share — plain link, QR (guide), or bio page.
- Measure — clicks, devices, referrers, countries, daily series.
- Optimize — A/B test destinations, retire dead campaigns, migrate what you imported from other tools.
Scaling to millions of links
The architecture that handles 10M links and 100M clicks looks different from the starter setup, but the changes are incremental, not structural:
Code space. Seven base62 characters are 3.5 trillion combinations. At 10M links you've used ~0.0003% of the space — collisions stay rare. The real constraint is the index: lookups must hit a primary index on shortCode, and hot links should be served from cache. The database index exists from day one; the cache layer is the first scaling upgrade.
Redirect path. The redirect route is read-heavy and cacheable: code → destination is a tiny key-value read. Edge caching with short TTLs (30–60s) absorbs the long tail; cache misses fall through to the database with a single indexed query. The performance budget section above applies at every scale — the p95 number is the product.
Analytics path. Click events are writes — the growth axis. The pattern that scales: writes go to the event store immediately (they never block redirects), daily rollups aggregate counts per link per day, and dashboards read rollups instead of raw events. This is the tiered storage model described in the analytics retention guide: raw events for the near term, rollups forever, totals for dashboards.
Multi-instance. When one process becomes two, two things must be shared: the database (obviously) and the rate-limit state. In-memory rate limiting works per instance; distributed deployments move the buckets to Redis/Valkey. The API contract — status codes, headers, error shapes — is identical either way, which is why the API docs never mention instance count.
The link lifecycle in production
A link in a serious platform has a lifecycle, not a creation date:
created → published → measured → optimized → expired → archived/deleted
- Published — the link enters campaigns, print, and templates. This is when aliases matter most: branded slugs convert better and survive contact with the real world.
- Measured — bot-filtered clicks, devices, referrers (analytics guide). A link without measurement is a guess.
- Optimized — destinations get A/B tested, tags get cleaned, timing gets tuned.
- Expired — time-based or click-based expiry ends the public life deliberately (expiry strategy), returning 410 instead of redirecting forever.
- Archived — the record (and its analytics history) remains in the dashboard for review; CSV export makes the archive portable.
Teams that manage the whole lifecycle get a link library that stays searchable and honest; teams that stop at creation get a graveyard. The organization workflow is the operating manual for the lifecycle.
Building your own vs using a platform
The DIY temptation is real — a shortener is "just a table and a redirect." The hidden costs appear at the edges:
| Capability | DIY hidden cost |
|---|---|
| Collision-safe codes | CSPRNG + retry + unique index — done, but audited how? |
| Bot filtering | User-agent lists rot; false counts erode trust in the numbers |
| Password gates | bcrypt + rate limiting + no-password-in-URL discipline |
| Print-grade QR | Resolution, quiet zone, error correction, SVG support |
| Rate limiting | Distributed-safe buckets, Retry-After semantics |
| Security headers | CSP, HSTS, and the rest of the hardening checklist |
Each edge is a small project. Five edges is a platform. For most teams the rational move is a managed link layer with an open API — which is exactly the slot yas.sh fills — and the API reference shows the contract you'd otherwise be building.
Conclusion
Short links are simple on the surface and full of traps underneath: status code semantics, code-space governance, bot noise, and latency budgets. A production platform handles all four by design — that's what separates a link infrastructure from a link gadget.
Try it live on the dashboard (free plan, no card), or read the full API reference and build your own integration in an afternoon.
