A subscription site we work on gates its paid reports properly. Sign in and you read them; do not and you get a sign-in page. We checked that, and it held.
Then we looked at what the public page said about a paid report, and found its actual contents.
Where it got out
A report has a page. That page was gated. But three other things on the site were built from the same text:
- The page's own meta description, which is what a search engine shows in results.
- The card on the archive listing, which showed a preview line.
- The RSS feed, which carried an excerpt of every item.
All three were generated by taking the first stretch of the report and trimming it. For a free article that is exactly right. For a paid one it meant the headline finding, with its numbers, was sitting in a tag built for crawlers.
The door was locked. The summary on the door repeated what was inside.
Why it is easy to miss
Nobody decided to leak anything. Each of those three features was built at a different time, by someone solving a different problem, and each one reasonably reached for “the start of the text”.
The gate was added later, on the page. Pages are what you think of as the thing being protected, because a page is what a person visits. A meta tag is not a page, and neither is a feed, so neither got reviewed.
The general shape
Every surface that summarises protected content is a copy of it. In most businesses there are more of these than anyone expects:
- Email notifications that include the first lines of a restricted document.
- Search results inside an application showing snippets from records the searcher cannot open.
- Calendar invitations whose titles carry the matter name.
- Automated reports mailing a summary table to a wider list than the detail goes to.
- Error messages and support tickets quoting the record that caused them.
- Link previews generated when someone pastes an internal URL into a chat tool.
What to do
The fix on our side was one function. Everywhere the site describes a report, it now asks whether that report is paid and returns a fixed line if it is, rather than reaching for the text. One place to change, one place to get right.
- List every surface that renders part of a protected record — page, preview, notification, feed, export, search result, log.
- Make them all go through one function that knows the access rule, instead of each reaching for the content.
- Test as a stranger, not as an administrator. Fetch the page signed out and read what actually comes back.
- Check the parts a person never sees. The tag, the feed, the JSON an API returns.
If you sell access to something, this is worth an hour. The page is almost never where it gets out.