URL Aesthetics vs. Security: `/users/101` vs `/users/cuid`
Balancing clean URLs with the security risk of ID enumeration
Vanity URLs Are Overrated
Why ugly IDs make for secure APIs.
The 'Peeping Tom' Attack
- The Scenario — You sign up for a new service. Your profile URL is
/users/1050. - The Curiosity — What happens if you change that number to
/users/1049? - The Breach — If the API returns someone else's data, you have an IDOR vulnerability.
The Threat: ID Enumeration
Scraping your entire business in 10 minutes.
- The Script — Write a loop from 1 to 10,000. Send a GET request for every number.
- The Leak — Now I have your entire customer database. Names, emails, activity.
- Business Intel — I can track exactly how many users you gain per day by watching the ID count rise.
The Fix: Use Ugly IDs
- UUID / CUID — Instead of
1050, useclj2k...ora0eebc99.... - Unguessable — There is no 'next' number. You can't guess the ID of another user.
- Anti-Scraping — Enumeration becomes mathematically impossible.
But... Ugly URLs are Ugly?
- The Compromise — Users love pretty URLs. APIs love secure IDs.
- The Strategy — Use 'Slugs' for public pages, but keep IDs for the API logic.
Architecture: The Slug Pattern
site.com/u/john-doe
- Public Face — The URL shows
john-doe(The Slug). - Internal Look up — Database maps
slug: john-doe->id: a0ee-bc99. - API Calls — Frontend API calls still use the UUID:
GET /api/users/a0ee-bc99.
Defense in Depth
- Authorization — Even with UUIDs, you MUST check: 'Does Current User own this ID?'
- Rate Limiting — Block IPs that try to access 100 different profiles in 1 minute.
Summary: Obscurity + Auth
Sequential IDs are a feature for your competitors.
Don't make it easy for scrapers. Hide your primary keys.
Check your browser URL bar right now. Is it an ID or a hash?