smawl
A self-hosted, serverless URL shortener built on Node.js and Vercel KV. SHA-256 hashing, idempotent short IDs, multi-tier rate limiting, and a clean REST API. Zero infrastructure cost.
Why Another URL Shortener?
Most URL shorteners are a binary choice: a closed SaaS with opaque data policies and vendor lock-in, or an open-source tool that demands a dedicated database server and full-stack infrastructure. smawl fills the gap between the two.
Zero hosting cost
Vercel's free tier handles serverless functions and KV storage for real-world traffic.
One command deploy
vercel deploy. No servers to
provision, no containers, no databases to manage.
Fully auditable
Collision handling, URL canonicalization, and rate limiting are all tested and open source.
Deterministic Short IDs
Short IDs are generated by running the normalized URL through SHA-256,
taking the first 5 bytes of the digest, and encoding them in Base62
(a–z A–Z 0–9). The result is left-padded to 6 characters and capped at 8.
The same URL always produces the same ID. Enabling idempotency without a lookup table query.
Idempotency & Collision Safety
Submitting the same URL twice returns the same short link without creating a duplicate KV entry. When a generated ID collides with an existing different URL, the algorithm appends an incrementing nonce and re-hashes recursively until a free slot is found.
Multi-Tier Rate Limiting
Counters live in Vercel KV. Globally consistent across all serverless instances. Falls back gracefully to in-memory when KV is unavailable.
| Client type | Limit | Window |
|---|---|---|
| Default / anonymous | 30 req | 1 min |
| Whitelisted API key / IP | 100 req | 1 min |
| Trusted app referrer | 300 req | 1 min |
URL Canonicalization
Before hashing, every URL is normalized so variations of the same address don't create duplicate entries:
- ▸ Protocol and hostname lowercased
- ▸ Trailing slashes stripped from paths
- ▸ Query strings and hash fragments preserved
- ▸Only
httpandhttpsaccepted.javascript:anddata:schemes rejected - ▸ URLs longer than 2,048 characters rejected
API Reference
Two endpoints. That's it.
/api/shorten
Shortens a URL. Returns the same short URL for the same input (idempotent). Optionally accepts a custom alias.
Request body
{
"url": "https://example.com/very/long/path", // required
"customId": "myAlias" // optional, 3–10 alphanumeric
}
Success: 200 OK
{ "shortUrl": "https://smawl.vercel.app/myAlias" }
Error: 4xx
{ "error": "Custom ID must be 3-10 alphanumeric characters" }
/:shortId
Resolves a short ID and issues an HTTP 302 redirect to the original URL. Unknown IDs return an HTML error page. Not a bare 404.
HTTP/1.1 302 Found
Location: https://example.com/very/long/path
Code Examples
The API is designed to be called programmatically from any language.
cURL
curl -X POST https://smawl.vercel.app/api/shorten \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/very/long/path"}'
JavaScript
const res = await fetch('https://smawl.vercel.app/api/shorten', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://example.com/very/long/path' }),
});
const { shortUrl } = await res.json();
console.log(shortUrl); // https://smawl.vercel.app/aBcDe1
Python
import requests
res = requests.post(
'https://smawl.vercel.app/api/shorten',
json={'url': 'https://example.com/very/long/path'}
)
print(res.json()['shortUrl'])
Architecture
Lean by design. Two serverless functions, one KV store, and a static frontend.
| File | Role |
|---|---|
api/shorten.js |
Serverless entry point for POST /api/shorten |
api/[shortId].js |
Serverless entry point for GET /:shortId |
src/shorten.js |
Core logic: validation, SHA-256 hashing, deduplication, KV storage |
src/rateLimit.js |
Multi-tier rate limiting with KV backend and in-memory fallback |
public/ |
Static frontend (HTML + vanilla JS) |
server.js |
Local Express dev server mirroring Vercel routing at localhost:3000
|
tests/ |
Jest suite. Validation, hashing, Base62, collision, idempotency, rate limiting |
Who It's For
- ▸ Developers embedding short-link generation into their own apps via the REST API.
- ▸ Teams who want a self-hosted shortener with configurable rate limits and trusted-app whitelisting.
- ▸ Learners exploring a real-world serverless Node.js project with Redis integration and collision-safe hashing.
- ▸ Privacy-conscious users who want full control over where URL data lives and for how long.
Self-Host in Minutes
Requires Node.js v18+ and a Vercel account with KV enabled. Without KV credentials, URL mappings fall back to process memory. Fine for local testing.
git clone <repo-url>
cd smawl && npm install
cp .env.local.example .env.local
# fill in KV_REST_API_URL + KV_REST_API_TOKEN
npm start
# Deploy to Vercel
vercel deploy
Run the test suite with npm test or npm test -- --coverage.
Security Design
-
▸
Only
httpandhttpsprotocols are accepted.javascript:anddata:URI schemes are rejected at validation time, preventing open-redirect abuse. -
▸
Generated short IDs derive solely from the SHA-256 hash of the normalized URL. They are
never user-controlled unless a
customIdis explicitly provided and validated. - ▸ Rate limiting is enforced server-side in Vercel KV and cannot be bypassed by client-side tricks or distributing requests across browser tabs.
- ▸ No user accounts or credentials are stored. The only data persisted in KV is the URL mapping itself.
Try It or Fork It
The live instance is at smawl.vercel.app. The source is on GitHub. Fork it, deploy your own instance, or embed the API in your next project.