Why Concealing IDs in URLs Isn't Safe (Vue.js + FastAPI)
Introduction

Software Developer I build modern SaaS tools with Vue.js & FastAPI · Sharing tutorials, dev logs, and business insights for solo devs & makers. Building in public.
When building web applications, especially SPAs with frameworks like Vue.js, it’s very common to expose resource identifiers in URLs:
/offers/32
At some point, many developers feel uncomfortable with this and try to hide or obfuscate IDs on the frontend, thinking it improves security.
I did the same.
In this article, I’ll explain why hiding IDs in frontend URLs is not real security, what risks it introduces, and how to handle this problem properly using FastAPI, UUIDs, and backend permissions.

The Common Misconception
A frequent assumption is:
“If users can’t see or guess the ID, they won’t be able to access other resources.”
This often leads to frontend-only solutions like:
Encoding IDs in Base64
Hashing IDs in JavaScript
Mapping numeric IDs to random strings in the UI
While this may look cleaner, it does not provide real security.
Why Frontend Obfuscation Is Not Security
1. The Frontend Is Not a Trusted Layer
Anything running in the browser can be:
inspected
modified
replayed
Even if you hide the ID in the URL, the real identifier:
still reaches the backend
still appears in network requests
can be extracted from API responses
If your backend accepts it, an attacker can reuse it.
2. Predictable Access Patterns Still Exist
Changing this:
/offers/32
to something like:
/offers/7f3a9c
does not stop someone from:
intercepting a valid request
replaying it with another value
brute-forcing or enumerating resources
If the backend does not enforce ownership and permissions, the resource is still vulnerable.
A Realistic Attack Scenario
Imagine this API endpoint:
GET /offers/{offer_id}
If your backend logic is:
“If the offer exists, return it.”
Then anyone who gets a valid ID (hidden or not) can access data they should not see.
Security never lives in the URL.
What I Changed in My Project
In my case:
I initially used numeric IDs (
/offers/32)I later switched to UUIDs
I already had backend permission checks
This improved things — but only because of what happened on the backend, not because the ID looked nicer.
Why UUIDs Are Better (But Still Not Enough Alone)
Switching to UUIDs helps because:
they are hard to guess
they reduce enumeration risks
they look opaque to users
Example:
/offers/550e8400-e29b-41d4-a716-446655440000
However:
⚠️ UUIDs are not a security mechanism by themselves.
If your API returns a resource without checking permissions, a UUID won’t save you.
The Correct Approach: Backend-Centered Security
1. Always Enforce Permissions in the Backend
With FastAPI, every protected endpoint should verify:
who the user is
what they are allowed to access
Example (simplified):
@app.get("/offers/{offer_id}")
def get_offer(offer_id: UUID, user=Depends(get_current_user)):
offer = get_offer_from_db(offer_id)
if offer.owner_id != user.id:
raise HTTPException(status_code=403, detail="Forbidden")
return offer
This check is non-negotiable.
2. Treat IDs as Public Identifiers
Assume that:
IDs will leak
URLs will be shared
requests will be replayed
Design your API so that even with a valid ID, unauthorized access is impossible.
3. Use UUIDs for Exposure, Internal IDs for Internals
A solid pattern is:
internal numeric IDs (database performance)
external UUIDs (API exposure)
This gives you:
clean URLs
safer public identifiers
flexibility in your data model
Frontend Best Practices (Vue.js)
On the frontend side:
keep URLs simple and readable
don’t rely on obfuscation for protection
assume the backend is the source of truth
Vue.js should consume the API, not protect it.
Key Takeaways
Hiding IDs in frontend URLs is not security
The frontend is never a trusted layer
UUIDs help, but don’t replace permissions
Real security lives in the backend
FastAPI makes permission enforcement explicit and clean
Conclusion
If you’re spending time trying to “secure” your application by hiding IDs in Vue.js, you’re solving the wrong problem.
Focus on:
authorization
ownership checks
backend validation
Your future self (and your users) will thank you.
If you found this useful, feel free to share it or connect with me on LinkedIn.



