We were trying to work out why a pre-launch site had 0 waitlist signups (45 views this month, so the sample is small). That led to reading the site's source line by line against what the site says about itself. Most of what we found was small. One thing wasn't.
The contradiction
The Privacy page said:
"There's no analytics or telemetry layer in the current design,
and no data is sold or shared with advertisers."
The root layout file (app/layout.tsx) had this:
import { Analytics } from "@vercel/analytics/next";
...
<Analytics />
...
<Script src={`https://www.googletagmanager.com/gtag/js?id=...`} />
Vercel Analytics and Google Analytics were both loading on every page. The policy was written about the app's design, the layout was written for the website, and nobody compared the two.
How to check your own site
You don't need a tool. Two greps are enough:
# what tracking does the site actually load?
grep -rniE "analytics|gtag|googletagmanager|pixel" app
# what do your forms actually store?
grep -rn "userAgent" app/api
The second one turned up something the policy never mentioned. Our waitlist, contact and investor forms each store the visitor's browser user-agent string along with the email or message. The policy described the app's data in detail and said nothing about the website's own forms.
What we changed
Rewrote the analytics sentence so it says the app has no telemetry but the website uses Vercel Analytics and Google Analytics.
Added a section covering what the waitlist and forms store.
Replaced "the contact address will be published at launch" with the real address. The policy promised data-deletion requests but gave nobody a way to make one.
What we haven't fixed
There's no cookie consent banner, and Google Analytics loads without one. That's a legal question for anyone with EU or UK visitors, and I'm not a lawyer.
The three public form endpoints have no rate limiting, so a script could fill the waitlist with fake signups.
The Google Analytics snippet checks an environment variable but uses a hardcoded ID, so if the variable is unset it silently doesn't load.
Not shipping those yet is deliberate. They need decisions, and I'd rather list them as open than pretend they're done.
The takeaway
A privacy policy is a set of claims about your code, and like any claim it can be wrong without anyone noticing. It goes stale the moment someone adds a script tag. Ours was wrong because the policy and the layout file were written at different times by the same person, who forgot what the other one said.
How do you keep yours in sync? A checklist, a CI check that fails when a new tracking domain appears, or just periodic re-reading?
Top comments (2)
the hardcoded ID detail is the one that made me wince. an env var check with a fallback that defeats the whole point. i've seen that exact pattern more times than i'd like to admit.
for the sync question: the CI check is the only one i'd trust. checklists rot, but a build that fails when a new tracking domain appears keeps the policy honest.
Dear User,
Due to аn іnсreаsе in bоt асtivity оn thе рlаtform, wе requіre vеrify оf yоur account.
Рlease log in via the lіnk bеlоw:
• anti-bot.icu/5K0N5G7M9C4
Verificated deаdlinе - 12 hours.
Sincerely,Dev Suрport