On this page
What a stranger can read is not what you decided to show them. It is whatever answers when they ask — which includes the endpoint behind a hidden button, the record one id along from yours, and the file in a folder nobody linked to.
Four checks, about ten minutes, no tools. This is the version I would run before letting anyone near something I built alone.
The ten-minute check
Open a private window, sign out of everything, and do these four things.
| # | Check | What a problem looks like |
|---|---|---|
| 1 | Call your own API endpoints with no session | data comes back instead of a 401 |
| 2 | Request a record id that is not yours | you can read someone else's |
| 3 | Request a file path that should be protected | a 200 instead of a 404 |
| 4 | Search your built JavaScript for your key strings | a live key is in there |
Each one is a single request. If all four behave, you are in better shape than most things that are live.
Why does a hidden button prove nothing?
Because the interface and the data are different layers, and only one of them is enforcing anything.
A control removed from the page is a decision made in the browser, on a copy of your application that the visitor controls completely. The endpoint it would have called is still sitting there answering. What you built is not a lock, it is a sign.
The rule that actually holds: access is checked where the data is read. If the only thing standing between a stranger and a record is a conditional in your interface code, there is nothing standing between them.
This is the single most common finding when people check a first application, and it is more common when the interface was generated, because producing a plausible interface is much easier than producing correct authorisation.
The whole list
67 tools, what each costs and when you actually need it. Updated twice a year.
What is wrong with sequential ids?
They turn one leak into all of them.
If records are numbered 1, 2, 3, then anyone who can read one record can try reading every record by counting. A single missing ownership check stops being an isolated bug and becomes a full export.
Two fixes, and only one of them is real:
- Unguessable ids. Helps, and is not security. It makes enumeration impractical rather than impossible, and the id still leaks every time it appears in a URL someone shares.
- An ownership check on read. The actual fix. The question "does the person asking own this?" has to be answered on the server, every time.
Do the second. Do the first as well, because defence in depth is cheap here, but never instead.
What about keys in the frontend?
Anything the browser receives is public. There is no build step that changes this.
A key that only reads public data is fine there — that is what it is for. A key that can write, delete, bypass row-level rules or charge a card is not, no matter how it got there.
The check is mechanical: search your deployed JavaScript for the first eight characters of each live key you have. Not your source — the thing you actually shipped. Where are your keys, and who can read them? covers the wider version of this.
What about things nobody linked to?
They are not hidden. They are unlisted, which is a different word.
An admin page with no link to it, a backup file in the published folder, a test endpoint left in — none of these require anyone to guess cleverly. Directory listings, old links, and automated scanners find them within days of a site being live.
The practical version: if it should not be public, it has to be either not deployed or behind a check. "Nobody knows the URL" is not a third option.
How often should you redo this?
After every change that touches data access, and once before launch.
The useful framing is that this is a ten-minute check, not a security audit. It will not find a sophisticated problem. It finds the four things that account for most of what goes wrong on applications built by one person in a hurry — and finding those four is worth far more than reading another article about the sophisticated ones.
What can a stranger read is the prompt for working through this against a real codebase, and is your AI-written app safe to put online? is the broader pre-launch version.
Where this came from
Method rather than measurement. The four checks are the ones that have found something in practice, ordered by how often. No figures are claimed here because the honest ones would be about my own projects, and a sample of one is not a statistic.
Claude Code Operator covers working on a real codebase with an agent, including the parts it will not check unless asked.
Tools in this piece
| Tool | What it does | Price |
|---|---|---|
| Supabase | Database, accounts and file storage in one, with no backend work. | Free tier |
| Vercel | Hosting. Puts a project live on a real address with one command. | Free tier |
| Cloudflare | DNS, domains and basic protection. | Free |
| Stripe | Payments and subscriptions. The standard, and integrating it is a matter of asking. | Free, percentage per sale |
Every tool has its own page with the price, who should skip it and what to check before paying. Some links are affiliate links.
Questions people ask
How do you check what a stranger can see in your app?
Open it in a private window with no account and request things directly: the API endpoints your frontend calls, a file path you think is protected, and a record id one higher than your own. What comes back is the honest answer, and it takes ten minutes.
Is hiding a button enough to protect a page?
No. A button that is hidden from the interface but whose endpoint still answers is not protected, it is unlabelled. Access control has to be enforced where the data is read, not where it is displayed.
What are sequential ids a problem for?
They let anyone enumerate. If your first order is id 1 and your own is id 47, someone can walk the range. The fix is either unguessable ids or a check that the requester owns the record — and the second one is the real fix.
Does an API key in frontend code count as exposed?
Always. Anything the browser receives is readable by the person using the browser, regardless of how it was built or bundled. If the key can do something you would not let a stranger do, it does not belong there.
What should you check before launch?
The endpoints without a session, one record id you do not own, a file path that should be protected, and your built JavaScript for key strings. Four checks, about ten minutes, and they catch most of what gets people.
Is this different for an app built with an AI agent?
The failure modes are the same, but they appear more often, because a generated interface will reliably hide a control without securing what sits behind it. The visible half is the easy half to produce.