
- If short codes are direct encodings of a monotonically increasing counter (e.g., Base62-encoding of 1,000,000), an attacker who decodes a single code can enumerate adjacent IDs and discover links that were never shared.
- Many Base62/decoder tools exist, making this a practical and trivial attack vector.

- Apply a reversible, secret permutation to the counter value before encoding. This preserves the counter’s uniqueness while hiding the sequence order from attackers.
- The mapping must be bijective (one-to-one and onto) so every counter value maps to a unique shuffled value and can be reversed when needed.
The affine example above is educational. For production consider stronger, well-reviewed approaches (e.g., format-preserving encryption, Feistel networks, or block-cipher-based permutations) and treat the permutation parameters as secrets. Rotate and protect them like cryptographic keys.
- The transform is reversible — you can compute the inverse mapping when resolving short codes.
- Different counter values map to different shuffled values, preserving uniqueness and avoiding collisions.
- From an attacker’s perspective the codes look random and are not trivially enumerable.
- Protect the permutation parameters (A, B, seed) as you would any cryptographic secret.
- If two requests read the counter simultaneously and both use the same value before increment, both will generate the same short code. One mapping will overwrite or fail — producing duplicate short codes or lost links.

- Use an atomic database sequence (Postgres sequences are safe under concurrency).
- Use an atomic insert that returns the generated ID (e.g.,
SERIAL/IDENTITYwithINSERT ... RETURNING). - Use optimistic inserts with a unique constraint on the short code and retry on conflict.
- For very high write throughput, allocate ID ranges to multiple writers or use a distributed ID generator (Snowflake-style).
Concise design summary
- One Postgres table maps
short_code→long_url. - Create links by reading a global counter, shuffling the counter with a reversible secret function, then Base62-encoding the result.
- Seven-character Base62 codes yield up to 62^7 (~3.5 trillion) unique values — adequate for many systems.
- Index the
short_codecolumn for fast resolution.

- Your URL shortener is typically read-heavy: ~1,000 reads per write is a common ratio. A direct app-server → Postgres lookup per redirect cannot scale under sudden spikes (e.g., a celebrity post).
- Use caching and edge delivery to absorb read traffic and mitigate hot-key spikes.

- Serve redirects via CDN edge caching or a reverse proxy with long-lived caches for popular links.
- Cache
short_code→long_urlmappings in an in-memory cache (e.g., Redis) using a cache-aside pattern. - Set appropriate
Cache-Controlheaders; 301 responses are cacheable by default, 302 may need explicit caching headers. - Use short TTLs for ordinary items and longer TTLs for hot items; pre-warm or pin extremely hot keys.
- Apply rate limiting, DDoS protections, and CDN-level safeguards to protect origin services.
- Keep redirect responses lightweight (HTTP 301/302) and avoid expensive logic on the hot path.
To avoid duplicate IDs under concurrency, prefer atomic database sequences (e.g., Postgres
nextval) or a distributed ID generator. For read-heavy workloads, prioritize CDN/edge caching and a Redis cache to absorb traffic spikes.- PostgreSQL Sequences
- Base62 encoding overview
- Snowflake ID generator (Twitter)
- Format-preserving encryption and Feistel networks (search for vetted libraries in your language of choice)